From 5d6971fb7f52678bb1d6ebe2a713d7d92aafaa1b Mon Sep 17 00:00:00 2001 From: Fedor Dudinsky Date: Mon, 29 Jul 2024 16:38:17 +0200 Subject: [PATCH] QQE-844 | Update dependencies - Change all dependencies to the latest versions - Add a bit of debugging related to https://github.com/quarkusio/quarkus/issues/42237 and https://github.com/quarkusio/quarkus/issues/42248 - After 3.13.0 quarkus OTEL exporter uses GRPC streams to export traces. Our hacked together OTEL server could not handle that and was rewritten to conform to OTEL specifications more. --- pom.xml | 39 +++--- .../startstop/ArtifactGeneratorBOMTest.java | 1 + .../ts/startstop/ArtifactGeneratorTest.java | 2 +- .../ts/startstop/SpecialCharsTest.java | 19 +-- .../quarkus/ts/startstop/StartStopTest.java | 31 ++--- .../quarkus/ts/startstop/utils/Commands.java | 2 +- .../io/quarkus/ts/startstop/utils/Logs.java | 9 +- .../utils/OpenTelemetryCollector.java | 123 ++++++++++++------ .../ts/startstop/utils/WhitelistLogLines.java | 4 +- 9 files changed, 143 insertions(+), 87 deletions(-) diff --git a/pom.xml b/pom.xml index 711609ec..0dc608a3 100644 --- a/pom.xml +++ b/pom.xml @@ -7,27 +7,28 @@ pom Quarkus StartStop TS: Parent - 3.11.0 + 3.13.0 io.quarkus - 3.11.0 + 3.13.0 17 17 17 - 3.10.1 - 2.22.2 - 2.22.2 - 3.3.0 + 3.13.0 + 3.3.1 + 3.3.1 + 3.6.0 UTF-8 - 5.9.2 - 2.11.0 - 3.12.0 - 3.5.0.Final - 2.19.0 - 1.30.0 - 4.5.7 - 2.22.0 - 1.8.0 - 3.2.2 + 5.10.3 + 2.16.1 + 3.15.0 + 3.6.0.Final + 2.23.1 + 1.45.0 + 4.5.9 + 2.24.1 + 1.9.0 + 3.3.1 + 1.3.2-alpha generator,startstop,bomtests,codequarkus,special-chars @@ -60,6 +61,12 @@ ${playwright.version} test + + io.opentelemetry.proto + opentelemetry-proto + ${opentelemetry-proto.version} + test + diff --git a/testsuite/src/it/java/io/quarkus/ts/startstop/ArtifactGeneratorBOMTest.java b/testsuite/src/it/java/io/quarkus/ts/startstop/ArtifactGeneratorBOMTest.java index f53c7e47..6bc806e3 100644 --- a/testsuite/src/it/java/io/quarkus/ts/startstop/ArtifactGeneratorBOMTest.java +++ b/testsuite/src/it/java/io/quarkus/ts/startstop/ArtifactGeneratorBOMTest.java @@ -86,6 +86,7 @@ public void testRuntime(TestInfo testInfo, String[] extensions, Set f Files.createDirectories(Paths.get(repoDir)); //Generator + LOGGER.info("Running inside " + appDir.getAbsolutePath()); LOGGER.info(mn + ": Generator command " + String.join(" ", generatorCmd)); generateLog = new File(logsDir + File.separator + "bom-artifact-generator.log"); ExecutorService buildService = Executors.newFixedThreadPool(1); diff --git a/testsuite/src/it/java/io/quarkus/ts/startstop/ArtifactGeneratorTest.java b/testsuite/src/it/java/io/quarkus/ts/startstop/ArtifactGeneratorTest.java index 5ae54eaf..9c17d755 100644 --- a/testsuite/src/it/java/io/quarkus/ts/startstop/ArtifactGeneratorTest.java +++ b/testsuite/src/it/java/io/quarkus/ts/startstop/ArtifactGeneratorTest.java @@ -268,7 +268,7 @@ public void testRuntime(TestInfo testInfo, String[] extensions, Set f } else { LOGGER.info(mn + ": Testing setup: " + String.join(" ", generatorCmd)); } - + LOGGER.info("Running inside " + appDir.getAbsolutePath()); try { // Cleanup cleanDirOrFile(appBaseDir.getAbsolutePath()); diff --git a/testsuite/src/it/java/io/quarkus/ts/startstop/SpecialCharsTest.java b/testsuite/src/it/java/io/quarkus/ts/startstop/SpecialCharsTest.java index 3774cea2..df9f4610 100644 --- a/testsuite/src/it/java/io/quarkus/ts/startstop/SpecialCharsTest.java +++ b/testsuite/src/it/java/io/quarkus/ts/startstop/SpecialCharsTest.java @@ -60,8 +60,8 @@ public void testRuntime(TestInfo testInfo, Apps app, MvnCmds mvnCmds, String sub File appDir = new File(appDestDir, app.dir); File appPomXml = new File(appDir, "pom.xml"); File logsDir = new File(appDir, "special-chars-logs"); - String cn = testInfo.getTestClass().get().getCanonicalName(); - String mn = testInfo.getTestMethod().get().getName(); + String canonicalName = testInfo.getTestClass().get().getCanonicalName(); + String methodName = testInfo.getTestMethod().get().getName(); LOGGER.info("Testing app: " + app + ", mode: " + mvnCmds.toString() + ", on path " + appDestDir); try { // Clean target directory @@ -75,6 +75,7 @@ public void testRuntime(TestInfo testInfo, Apps app, MvnCmds mvnCmds, String sub } // Copy to path with special characters + LOGGER.info("Copying " + appBaseDir + "to" + appDestDir); FileUtils.copyDirectoryToDirectory(appBaseDir, appDestDir); // Replace relative path to parent project @@ -99,7 +100,7 @@ public void testRuntime(TestInfo testInfo, Apps app, MvnCmds mvnCmds, String sub baseBuildCmd.add("-Dquarkus.platform.group-id=" + getQuarkusGroupId()); cmd = getBuildCommand(baseBuildCmd.toArray(new String[0])); - appendln(whatIDidReport, "# " + cn + ", " + mn); + appendln(whatIDidReport, "# " + canonicalName + ", " + methodName); appendln(whatIDidReport, (new Date()).toString()); appendln(whatIDidReport, appDir.getAbsolutePath()); appendlnSection(whatIDidReport, String.join(" ", cmd)); @@ -111,11 +112,11 @@ public void testRuntime(TestInfo testInfo, Apps app, MvnCmds mvnCmds, String sub buildService.awaitTermination(30, TimeUnit.MINUTES); assertTrue(buildLogA.exists()); - checkLog(cn, mn, app, mvnCmds, buildLogA); + checkLog(canonicalName, methodName, app, mvnCmds, buildLogA); } // Run - runLogA = new File(logsDir + File.separator + subdir + "-" + mvnCmds.name().toLowerCase() + "-run.log"); + runLogA = new File(logsDir + File.separator + subdir + "-" + mvnCmds.name().toLowerCase() + "-run.log"); if (mvnCmds == MvnCmds.DEV) { List baseBuildCmd = new ArrayList<>(); @@ -148,12 +149,12 @@ public void testRuntime(TestInfo testInfo, Apps app, MvnCmds mvnCmds, String sub // Archive logs if (buildLogA != null) { - archiveLog(cn, mn, buildLogA); + archiveLog(canonicalName, methodName, buildLogA); } if (runLogA != null) { - archiveLog(cn, mn, runLogA); + archiveLog(canonicalName, methodName, runLogA); } - writeReport(cn, mn, whatIDidReport.toString()); + writeReport(canonicalName, methodName, whatIDidReport.toString()); removeDirWithSpecialCharacters(appDestDir); } @@ -176,6 +177,7 @@ public void spacesNative(TestInfo testInfo) throws IOException, InterruptedExcep } @Test + @Disabled("TODO: https://issues.redhat.com/browse/QUARKUS-4695") public void specialJVM(TestInfo testInfo) throws IOException, InterruptedException { testRuntime(testInfo, Apps.JAKARTA_REST_MINIMAL, MvnCmds.JVM, ",;~!@#$%^&()"); } @@ -189,6 +191,7 @@ public void specialDEV(TestInfo testInfo) throws IOException, InterruptedExcepti @Test @Tag("native") @DisabledOnOs({OS.WINDOWS}) // https://github.com/quarkusio/quarkus/issues/9707 + @Disabled("TODO: https://issues.redhat.com/browse/QUARKUS-4695") public void specialNative(TestInfo testInfo) throws IOException, InterruptedException { testRuntime(testInfo, Apps.JAKARTA_REST_MINIMAL, MvnCmds.NATIVE, ",;~!@#$%^&()"); } diff --git a/testsuite/src/it/java/io/quarkus/ts/startstop/StartStopTest.java b/testsuite/src/it/java/io/quarkus/ts/startstop/StartStopTest.java index af96bde2..aa44ed5f 100644 --- a/testsuite/src/it/java/io/quarkus/ts/startstop/StartStopTest.java +++ b/testsuite/src/it/java/io/quarkus/ts/startstop/StartStopTest.java @@ -82,9 +82,9 @@ public void testRuntime(TestInfo testInfo, Apps app, MvnCmds mvnCmds, Supplier asyncProfiler = mvnCmds == MvnCmds.JVM ? AsyncProfiler.create() : Optional.empty(); - String cn = testInfo.getTestClass().get().getCanonicalName(); - String mn = testInfo.getTestMethod().get().getName(); - try(var testResource = testResourceSupplier.get()) { + String canonicalName = testInfo.getTestClass().get().getCanonicalName(); + String methodName = testInfo.getTestMethod().get().getName(); + try (var testResource = testResourceSupplier.get()) { // Cleanup asyncProfiler.ifPresent(ignore -> AsyncProfiler.cleanProfilingResults(app)); cleanTarget(app); @@ -99,9 +99,10 @@ public void testRuntime(TestInfo testInfo, Apps app, MvnCmds mvnCmds, Supplier control.stopProfing(appDir, mvnCmds, currentProcess, runId)); + asyncProfiler.ifPresent(control -> control.stopProfing(appDir, mvnCmds, currentProcess, runId)); LOGGER.info("Testing web page content..."); for (String[] urlContent : app.urlContent.urlContent) { @@ -166,12 +167,12 @@ public void testRuntime(TestInfo testInfo, Apps app, MvnCmds mvnCmds, Supplier profiler.archiveProfilingResults(cn, mn, appDir)); - archiveLog(cn, mn, buildLogA); - archiveLog(cn, mn, runLogA); - writeReport(cn, mn, whatIDidReport.toString()); - if ( !disableCleanup() ){ + asyncProfiler.ifPresent(profiler -> profiler.archiveProfilingResults(canonicalName, methodName, appDir)); + archiveLog(canonicalName, methodName, buildLogA); + archiveLog(canonicalName, methodName, runLogA); + writeReport(canonicalName, methodName, whatIDidReport.toString()); + if (!disableCleanup()) { cleanTarget(app); } } diff --git a/testsuite/src/it/java/io/quarkus/ts/startstop/utils/Commands.java b/testsuite/src/it/java/io/quarkus/ts/startstop/utils/Commands.java index fb805b7c..954eb46b 100644 --- a/testsuite/src/it/java/io/quarkus/ts/startstop/utils/Commands.java +++ b/testsuite/src/it/java/io/quarkus/ts/startstop/utils/Commands.java @@ -418,7 +418,7 @@ public static boolean waitForTcpClosed(String host, int port, long loopTimeoutS) return false; } - // TODO we should get rid of it once Quarkus progresses with walid config per extension in generated examples + // TODO we should get rid of it once Quarkus progresses with valid config per extension in generated examples public static void confAppPropsForSkeleton(String appDir) throws IOException { // Config, see app-generated-skeleton/README.md final String appRelativePath = diff --git a/testsuite/src/it/java/io/quarkus/ts/startstop/utils/Logs.java b/testsuite/src/it/java/io/quarkus/ts/startstop/utils/Logs.java index 046f5bd3..da81e66b 100644 --- a/testsuite/src/it/java/io/quarkus/ts/startstop/utils/Logs.java +++ b/testsuite/src/it/java/io/quarkus/ts/startstop/utils/Logs.java @@ -213,12 +213,16 @@ public static void archiveLog(String testClass, String testMethod, File log) thr if (files != null) { for (File file : files) { if (!file.isDirectory()) { - Files.copy(file.toPath(), Paths.get(destDir.toString(), file.getName()), REPLACE_EXISTING); + Path target = Paths.get(destDir.toString(), file.getName()); + LOGGER.info("Saving log to " + target); + Files.copy(file.toPath(), target, REPLACE_EXISTING); } } } } else { - Files.copy(log.toPath(), Paths.get(destDir.toString(), filename), REPLACE_EXISTING); + Path target = Paths.get(destDir.toString(), filename); + LOGGER.info("Saving log to " + target); + Files.copy(log.toPath(), target, REPLACE_EXISTING); } } @@ -228,6 +232,7 @@ public static void writeReport(String testClass, String testMethod, String text) Files.write(Paths.get(destDir.toString(), "report.md"), text.getBytes(UTF_8), StandardOpenOption.CREATE, StandardOpenOption.TRUNCATE_EXISTING); Path agregateReport = Paths.get(getLogsDir().toString(), "aggregated-report.md"); if (Files.notExists(agregateReport)) { + LOGGER.info("Saving report to " + agregateReport); Files.write(agregateReport, ("# Aggregated Report\n\n").getBytes(UTF_8), StandardOpenOption.CREATE); } Files.write(agregateReport, text.getBytes(UTF_8), StandardOpenOption.APPEND); diff --git a/testsuite/src/it/java/io/quarkus/ts/startstop/utils/OpenTelemetryCollector.java b/testsuite/src/it/java/io/quarkus/ts/startstop/utils/OpenTelemetryCollector.java index e5f456dc..18554aea 100644 --- a/testsuite/src/it/java/io/quarkus/ts/startstop/utils/OpenTelemetryCollector.java +++ b/testsuite/src/it/java/io/quarkus/ts/startstop/utils/OpenTelemetryCollector.java @@ -1,80 +1,77 @@ package io.quarkus.ts.startstop.utils; +import io.opentelemetry.proto.collector.trace.v1.ExportTracePartialSuccess; +import io.opentelemetry.proto.collector.trace.v1.ExportTraceServiceRequest; +import io.opentelemetry.proto.collector.trace.v1.ExportTraceServiceResponse; +import io.opentelemetry.proto.collector.trace.v1.TraceServiceGrpc; +import io.opentelemetry.proto.trace.v1.Span; import io.vertx.core.Vertx; import io.vertx.core.http.HttpServer; import io.vertx.core.http.HttpServerOptions; import io.vertx.core.http.HttpServerRequest; -import io.vertx.grpc.common.GrpcMessage; +import io.vertx.grpc.common.GrpcStatus; import io.vertx.grpc.server.GrpcServer; +import org.jboss.logging.Logger; import java.io.Closeable; import java.io.IOException; +import java.util.List; import java.util.concurrent.atomic.AtomicBoolean; /** * This simplistic collector allows us to test Vert.x-based traces exporter in Quarkus without starting a container. */ public class OpenTelemetryCollector implements UnitTestResource { - + private static final Logger LOGGER = Logger.getLogger(OpenTelemetryCollector.class.getName()); private static final String HELLO_ENDPOINT_OPERATION_NAME = "GET /hello"; private static final String GET_HELLO_INVOCATION_NOT_TRACED = HELLO_ENDPOINT_OPERATION_NAME + " invocation not traced"; /** * If you change this port, you must also change respective 'quarkus.otel.exporter.otlp.traces.endpoint' value. */ private static final int OTEL_COLLECTOR_PORT = 4317; + private static final int WEB_ENDPOINT = 16686; // Can be changed to anything, currently is the same as Jaeger. private static final String GET_HELLO_TRACES_PATH = "/recorded-traces/get-hello"; - static final String GET_HELLO_TRACES_URL = "http://localhost:" + OTEL_COLLECTOR_PORT + GET_HELLO_TRACES_PATH; static final String GET_HELLO_INVOCATION_TRACED = HELLO_ENDPOINT_OPERATION_NAME + " invocation traced"; + static final String GET_HELLO_TRACES_URL = "http://localhost:" + WEB_ENDPOINT + GET_HELLO_TRACES_PATH; - private final Closeable closeable; - private final RequestHandler requestHandler; + private final Vertx vertx; + private final Closeable backEnd; + private final Closeable frontEnd; + private final AtomicBoolean helloEndpointCallTraced = new AtomicBoolean(false); public OpenTelemetryCollector() { - this.closeable = createGrpcServer(); - this.requestHandler = new RequestHandler(); - } - - private Closeable createGrpcServer() { - Vertx vertx = Vertx.vertx(); - GrpcServer grpcProxy = GrpcServer.server(vertx); - - // record incoming traces - grpcProxy.callHandler(reqFromQuarkus -> reqFromQuarkus.messageHandler(requestHandler::onReceivedTraces)); - - HttpServer httpServer = vertx.createHttpServer(new HttpServerOptions().setPort(OTEL_COLLECTOR_PORT)); - httpServer.requestHandler(httpServerRequest -> { - if (httpServerRequest.path().contains(GET_HELLO_TRACES_PATH)) { - requestHandler.handleTracesRequest(httpServerRequest); - } else { - grpcProxy.handle(httpServerRequest); - } - }).listen(); - - // close resources - return () -> { - httpServer.close().toCompletionStage().toCompletableFuture().join(); - vertx.close().toCompletionStage().toCompletableFuture().join(); - }; + vertx = Vertx.vertx(); + this.backEnd = new GRPCTraceHandler(vertx); + this.frontEnd = new FrontEnd(vertx); } @Override public void close() throws IOException { - closeable.close(); + frontEnd.close(); + backEnd.close(); + vertx.close().toCompletionStage().toCompletableFuture().join(); } @Override public void reset() { - requestHandler.resetTraces(); + helloEndpointCallTraced.set(false); } - private static class RequestHandler { + private class FrontEnd implements Closeable { + private final HttpServer httpServer; - private final AtomicBoolean helloEndpointCallTraced = new AtomicBoolean(false); + public FrontEnd(Vertx vertx) { + httpServer = vertx + .createHttpServer() + .requestHandler(this::handleTracesRequest); + httpServer.listen(WEB_ENDPOINT); + } private void handleTracesRequest(HttpServerRequest request) { final String response; - if (helloEndpointCallTraced.get()) { + boolean isTraced = helloEndpointCallTraced.get(); + if (isTraced) { response = GET_HELLO_INVOCATION_TRACED; } else { response = GET_HELLO_INVOCATION_NOT_TRACED; @@ -82,18 +79,58 @@ private void handleTracesRequest(HttpServerRequest request) { request.response().end(response); } - private void onReceivedTraces(GrpcMessage exportedTraces) { - if (!helloEndpointCallTraced.get() && helloEndpointCallTraced(exportedTraces)) { - helloEndpointCallTraced.set(true); - } + @Override + public void close() { + LOGGER.info("Closing the server"); + httpServer.close().toCompletionStage().toCompletableFuture().join(); + LOGGER.info("The server was closed"); } + } - private void resetTraces() { - helloEndpointCallTraced.set(false); + class GRPCTraceHandler implements Closeable { + private final HttpServer httpServer; + + public GRPCTraceHandler(Vertx vertx) { + GrpcServer grpcHandler = GrpcServer.server(vertx); + + // record incoming traces + grpcHandler.callHandler(TraceServiceGrpc.getExportMethod(), request -> { + // https://vertx.io/docs/vertx-grpc/java/#_streaming_request, because Quarkus uses streaming since 3.13 + request.handler((ExportTraceServiceRequest tracesRequest) -> { + LOGGER.info("Processing traces"); + List traces = tracesRequest.getResourceSpansList().stream() + .flatMap(resourceSpans -> resourceSpans.getScopeSpansList().stream()) + .flatMap(scopeSpans -> scopeSpans.getSpansList().stream()) + .map(Span::getName) + .toList(); + + for (String trace : traces) { + if (trace.contains(HELLO_ENDPOINT_OPERATION_NAME)) { + LOGGER.info("Received trace for " + HELLO_ENDPOINT_OPERATION_NAME); + helloEndpointCallTraced.compareAndSet(false, true); + } + } + }); + request.endHandler(v -> { + // https://opentelemetry.io/docs/specs/otlp/#full-success + request.response().end(ExportTraceServiceResponse.newBuilder().build()); + }); + request.exceptionHandler(err -> { // https://opentelemetry.io/docs/specs/otlp/#failures + request.response().status(GrpcStatus.INVALID_ARGUMENT).end(); + }); + }); + httpServer = vertx + .createHttpServer() + .requestHandler(grpcHandler); + httpServer.listen(OTEL_COLLECTOR_PORT); + LOGGER.info("The listener started!"); } - private static boolean helloEndpointCallTraced(GrpcMessage msgFromQuarkus) { - return msgFromQuarkus.payload().toString().contains(HELLO_ENDPOINT_OPERATION_NAME); + @Override + public void close() { + LOGGER.info("Closing the listener"); + httpServer.close().toCompletionStage().toCompletableFuture().join(); + LOGGER.info("The listener was closed"); } } } diff --git a/testsuite/src/it/java/io/quarkus/ts/startstop/utils/WhitelistLogLines.java b/testsuite/src/it/java/io/quarkus/ts/startstop/utils/WhitelistLogLines.java index d1f6ab27..979134d9 100755 --- a/testsuite/src/it/java/io/quarkus/ts/startstop/utils/WhitelistLogLines.java +++ b/testsuite/src/it/java/io/quarkus/ts/startstop/utils/WhitelistLogLines.java @@ -105,7 +105,9 @@ public enum WhitelistLogLines { // https://github.com/quarkusio/quarkus/issues/38711 Pattern.compile(".*SplitPackageProcessor.*Following packages were detected in multiple archives.*"), // TODO: remove next line when 3.7.3 is released, see https://github.com/quarkusio/quarkus/pull/38710 - Pattern.compile(".*This instance of GraphiQLHandler has been created with a deprecated method.*") + Pattern.compile(".*This instance of GraphiQLHandler has been created with a deprecated method.*"), + // TODO: https://github.com/quarkusio/quarkus/issues/42237 + Pattern.compile(".*Failed to index org.springframework.aot.hint.annotation.Reflective.*") }), // Quarkus is not being gratefully shutdown in Windows when running in Dev mode. // Reported by https://github.com/quarkusio/quarkus/issues/14647.