From 6f32ecab24c7879089d85122f497d8b0b62299a2 Mon Sep 17 00:00:00 2001 From: Stuart Douglas Date: Thu, 9 Sep 2021 10:26:31 +1000 Subject: [PATCH] Fix lambda continuous testing failure If the tests start before the dev mode has initialized the system property would be seen by dev mode, and dev mode could open it's endpoint on 8081. This causes the tests to be run against the dev mode endpoint which breaks change tracking. Also fixes a few other minor things. --- .../dev/testing/TestTracingProcessor.java | 3 ++ .../DevServicesLambdaProcessor.java | 7 ++- .../runtime/AbstractLambdaPollLoop.java | 32 +++++++----- .../lambda/runtime/AmazonLambdaApi.java | 14 +++-- .../deployment/AmazonLambdaProcessor.java | 5 +- ...aDevServicesContinuousTestingTestCase.java | 5 +- .../lambda/runtime/MockEventServer.java | 51 ++++++++++++------- .../lambda/runtime/AmazonLambdaRecorder.java | 5 +- .../bindings/FunqyLambdaBuildStep.java | 5 +- .../lambda/FunqyLambdaBindingRecorder.java | 5 +- .../devmode/tests/TestsProcessor.java | 2 +- .../runtime/devmode/DevConsoleRecorder.java | 2 +- 12 files changed, 84 insertions(+), 52 deletions(-) diff --git a/core/deployment/src/main/java/io/quarkus/deployment/dev/testing/TestTracingProcessor.java b/core/deployment/src/main/java/io/quarkus/deployment/dev/testing/TestTracingProcessor.java index 7e2777caaa5e41..89daef853919dc 100644 --- a/core/deployment/src/main/java/io/quarkus/deployment/dev/testing/TestTracingProcessor.java +++ b/core/deployment/src/main/java/io/quarkus/deployment/dev/testing/TestTracingProcessor.java @@ -25,6 +25,7 @@ import io.quarkus.deployment.builditem.ServiceStartBuildItem; import io.quarkus.deployment.logging.LogCleanupFilterBuildItem; import io.quarkus.dev.spi.DevModeType; +import io.quarkus.dev.testing.ContinuousTestingSharedStateManager; import io.quarkus.dev.testing.TracingHandler; import io.quarkus.gizmo.Gizmo; @@ -82,6 +83,8 @@ void startTesting(TestConfig config, LiveReloadBuildItem liveReloadBuildItem, testSupport.stop(); } } + QuarkusClassLoader cl = (QuarkusClassLoader) Thread.currentThread().getContextClassLoader(); + ((QuarkusClassLoader) cl.parent()).addCloseTask(ContinuousTestingSharedStateManager::reset); } @BuildStep(onlyIf = IsTest.class) diff --git a/extensions/amazon-lambda/common-deployment/src/main/java/io/quarkus/amazon/lambda/deployment/DevServicesLambdaProcessor.java b/extensions/amazon-lambda/common-deployment/src/main/java/io/quarkus/amazon/lambda/deployment/DevServicesLambdaProcessor.java index 6b41e5100b0c96..e1fbd0fdb33cff 100644 --- a/extensions/amazon-lambda/common-deployment/src/main/java/io/quarkus/amazon/lambda/deployment/DevServicesLambdaProcessor.java +++ b/extensions/amazon-lambda/common-deployment/src/main/java/io/quarkus/amazon/lambda/deployment/DevServicesLambdaProcessor.java @@ -15,7 +15,7 @@ import io.quarkus.deployment.annotations.BuildProducer; import io.quarkus.deployment.annotations.BuildStep; import io.quarkus.deployment.annotations.Record; -import io.quarkus.deployment.builditem.DevServicesNativeConfigResultBuildItem; +import io.quarkus.deployment.builditem.DevServicesConfigResultBuildItem; import io.quarkus.deployment.builditem.LaunchModeBuildItem; import io.quarkus.deployment.builditem.ServiceStartBuildItem; import io.quarkus.runtime.LaunchMode; @@ -51,7 +51,7 @@ private boolean legacyTestingEnabled() { public void startEventServer(LaunchModeBuildItem launchMode, LambdaConfig config, Optional override, - BuildProducer devServicePropertiesProducer, + BuildProducer devServicePropertiesProducer, BuildProducer serviceStartBuildItemBuildProducer) throws Exception { if (!launchMode.getLaunchMode().isDevOrTest()) return; @@ -73,9 +73,8 @@ public void startEventServer(LaunchModeBuildItem launchMode, startMode = launchMode.getLaunchMode(); server.start(port); String baseUrl = "localhost:" + port + MockEventServer.BASE_PATH; - System.setProperty(AmazonLambdaApi.QUARKUS_INTERNAL_AWS_LAMBDA_TEST_API, baseUrl); devServicePropertiesProducer.produce( - new DevServicesNativeConfigResultBuildItem(AmazonLambdaApi.QUARKUS_INTERNAL_AWS_LAMBDA_TEST_API, baseUrl)); + new DevServicesConfigResultBuildItem(AmazonLambdaApi.QUARKUS_INTERNAL_AWS_LAMBDA_TEST_API, baseUrl)); Runnable closeTask = () -> { if (server != null) { try { diff --git a/extensions/amazon-lambda/common-runtime/src/main/java/io/quarkus/amazon/lambda/runtime/AbstractLambdaPollLoop.java b/extensions/amazon-lambda/common-runtime/src/main/java/io/quarkus/amazon/lambda/runtime/AbstractLambdaPollLoop.java index 1d7052ccc766df..eb861016d78eae 100644 --- a/extensions/amazon-lambda/common-runtime/src/main/java/io/quarkus/amazon/lambda/runtime/AbstractLambdaPollLoop.java +++ b/extensions/amazon-lambda/common-runtime/src/main/java/io/quarkus/amazon/lambda/runtime/AbstractLambdaPollLoop.java @@ -16,19 +16,23 @@ import com.fasterxml.jackson.databind.ObjectReader; import io.quarkus.runtime.Application; +import io.quarkus.runtime.LaunchMode; import io.quarkus.runtime.ShutdownContext; public abstract class AbstractLambdaPollLoop { private static final Logger log = Logger.getLogger(AbstractLambdaPollLoop.class); - private ObjectMapper objectMapper; - private ObjectReader cognitoIdReader; - private ObjectReader clientCtxReader; + private final ObjectMapper objectMapper; + private final ObjectReader cognitoIdReader; + private final ObjectReader clientCtxReader; + private final LaunchMode launchMode; - public AbstractLambdaPollLoop(ObjectMapper objectMapper, ObjectReader cognitoIdReader, ObjectReader clientCtxReader) { + public AbstractLambdaPollLoop(ObjectMapper objectMapper, ObjectReader cognitoIdReader, ObjectReader clientCtxReader, + LaunchMode launchMode) { this.objectMapper = objectMapper; this.cognitoIdReader = cognitoIdReader; this.clientCtxReader = clientCtxReader; + this.launchMode = launchMode; } protected abstract boolean isStream(); @@ -45,7 +49,7 @@ public void startPollLoop(ShutdownContext context) { @Override public void run() { try { - if (!LambdaHotReplacementRecorder.enabled) { + if (!LambdaHotReplacementRecorder.enabled && launchMode == LaunchMode.DEVELOPMENT) { // when running with continuous testing, this method fails // because currentApplication is not set when running as an // auxiliary application. So, just skip it if hot replacement enabled. @@ -81,7 +85,7 @@ public void run() { continue; } try { - if (LambdaHotReplacementRecorder.enabled) { + if (LambdaHotReplacementRecorder.enabled && launchMode == LaunchMode.DEVELOPMENT) { try { // do not interrupt during a hot replacement // as shutdown will abort and do nasty things. @@ -125,7 +129,7 @@ public void run() { if (abortGracefully(e)) { return; } - log.error("Failed to run lambda", e); + log.error("Failed to run lambda (" + launchMode + ")", e); postError(AmazonLambdaApi.invocationError(baseUrl, requestId), new FunctionError(e.getClass().getName(), e.getMessage())); @@ -134,7 +138,7 @@ public void run() { } catch (Exception e) { if (!abortGracefully(e)) - log.error("Error running lambda", e); + log.error("Error running lambda (" + launchMode + ")", e); Application app = Application.currentApplication(); if (app != null) { app.stop(); @@ -150,24 +154,24 @@ public void run() { } } catch (Exception e) { try { - log.error("Lambda init error", e); + log.error("Lambda init error (" + launchMode + ")", e); postError(AmazonLambdaApi.initError(baseUrl), new FunctionError(e.getClass().getName(), e.getMessage())); } catch (Exception ex) { - log.error("Failed to report init error", ex); + log.error("Failed to report init error (" + launchMode + ")", ex); } finally { // our main loop is done, time to shutdown Application app = Application.currentApplication(); if (app != null) { - log.error("Shutting down Quarkus application because of error"); + log.error("Shutting down Quarkus application because of error (" + launchMode + ")"); app.stop(); } } } finally { - log.info("Lambda polling thread complete"); + log.info("Lambda polling thread complete (" + launchMode + ")"); } } - }, "Lambda Thread"); + }, "Lambda Thread (" + launchMode + ")"); pollingThread.setDaemon(true); context.addShutdownTask(() -> { running.set(false); @@ -261,7 +265,7 @@ boolean abortGracefully(Exception ex) { // if we are running in test mode, or native mode outside of the lambda container, then don't output stack trace for socket errors boolean lambdaEnv = System.getenv("AWS_LAMBDA_RUNTIME_API") != null; - boolean testEnv = System.getProperty(AmazonLambdaApi.QUARKUS_INTERNAL_AWS_LAMBDA_TEST_API) != null; + boolean testEnv = LaunchMode.current() == LaunchMode.TEST; boolean graceful = ((ex instanceof SocketException || ex instanceof ConnectException) && testEnv) || (ex instanceof UnknownHostException && !lambdaEnv); diff --git a/extensions/amazon-lambda/common-runtime/src/main/java/io/quarkus/amazon/lambda/runtime/AmazonLambdaApi.java b/extensions/amazon-lambda/common-runtime/src/main/java/io/quarkus/amazon/lambda/runtime/AmazonLambdaApi.java index ec1e528ab38753..5b04cbf30bde53 100644 --- a/extensions/amazon-lambda/common-runtime/src/main/java/io/quarkus/amazon/lambda/runtime/AmazonLambdaApi.java +++ b/extensions/amazon-lambda/common-runtime/src/main/java/io/quarkus/amazon/lambda/runtime/AmazonLambdaApi.java @@ -3,6 +3,10 @@ import java.net.MalformedURLException; import java.net.URL; +import org.eclipse.microprofile.config.ConfigProvider; + +import io.quarkus.runtime.LaunchMode; + /** * Various constants and util methods used for communication with the AWS API. */ @@ -80,13 +84,15 @@ static String functionVersion() { } public static boolean isTestMode() { - return System.getProperty(AmazonLambdaApi.QUARKUS_INTERNAL_AWS_LAMBDA_TEST_API) != null; + //need this config check for native tests + return LaunchMode.current() == LaunchMode.TEST + || ConfigProvider.getConfig().getOptionalValue(QUARKUS_INTERNAL_AWS_LAMBDA_TEST_API, String.class).isPresent(); } private static String runtimeApi() { - String testApi = System.getProperty(QUARKUS_INTERNAL_AWS_LAMBDA_TEST_API); - if (testApi != null) { - return testApi; + var testApi = ConfigProvider.getConfig().getOptionalValue(QUARKUS_INTERNAL_AWS_LAMBDA_TEST_API, String.class); + if (testApi.isPresent()) { + return testApi.get(); } return System.getenv("AWS_LAMBDA_RUNTIME_API"); } diff --git a/extensions/amazon-lambda/deployment/src/main/java/io/quarkus/amazon/lambda/deployment/AmazonLambdaProcessor.java b/extensions/amazon-lambda/deployment/src/main/java/io/quarkus/amazon/lambda/deployment/AmazonLambdaProcessor.java index b755f0aae7f7b2..7b14804bf98346 100644 --- a/extensions/amazon-lambda/deployment/src/main/java/io/quarkus/amazon/lambda/deployment/AmazonLambdaProcessor.java +++ b/extensions/amazon-lambda/deployment/src/main/java/io/quarkus/amazon/lambda/deployment/AmazonLambdaProcessor.java @@ -240,9 +240,10 @@ public void recordHandlerClass(List lambdas, @Record(value = ExecutionTime.RUNTIME_INIT) void startPoolLoop(AmazonLambdaRecorder recorder, ShutdownContextBuildItem shutdownContextBuildItem, + LaunchModeBuildItem launchModeBuildItem, List orderServicesFirst // try to order this after service recorders ) { - recorder.startPollLoop(shutdownContextBuildItem); + recorder.startPollLoop(shutdownContextBuildItem, launchModeBuildItem.getLaunchMode()); } @BuildStep @@ -253,7 +254,7 @@ void startPoolLoopDevOrTest(AmazonLambdaRecorder recorder, LaunchModeBuildItem launchModeBuildItem) { LaunchMode mode = launchModeBuildItem.getLaunchMode(); if (mode.isDevOrTest()) { - recorder.startPollLoop(shutdownContextBuildItem); + recorder.startPollLoop(shutdownContextBuildItem, mode); } } diff --git a/extensions/amazon-lambda/deployment/src/test/java/io/quarkus/amazon/lambda/deployment/testing/LambdaDevServicesContinuousTestingTestCase.java b/extensions/amazon-lambda/deployment/src/test/java/io/quarkus/amazon/lambda/deployment/testing/LambdaDevServicesContinuousTestingTestCase.java index 1565016006b699..c0007951bd289f 100644 --- a/extensions/amazon-lambda/deployment/src/test/java/io/quarkus/amazon/lambda/deployment/testing/LambdaDevServicesContinuousTestingTestCase.java +++ b/extensions/amazon-lambda/deployment/src/test/java/io/quarkus/amazon/lambda/deployment/testing/LambdaDevServicesContinuousTestingTestCase.java @@ -6,7 +6,7 @@ import org.jboss.shrinkwrap.api.asset.StringAsset; import org.jboss.shrinkwrap.api.spec.JavaArchive; import org.junit.jupiter.api.Assertions; -import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.RepeatedTest; import org.junit.jupiter.api.extension.RegisterExtension; import io.quarkus.test.ContinuousTestingTestUtils; @@ -30,7 +30,8 @@ public JavaArchive get() { } }); - @Test + //run this twice, to make sure everything is cleaned up properly + @RepeatedTest(2) public void testLambda() throws Exception { ContinuousTestingTestUtils utils = new ContinuousTestingTestUtils(); var result = utils.waitForNextCompletion(); diff --git a/extensions/amazon-lambda/event-server/src/main/java/io/quarkus/amazon/lambda/runtime/MockEventServer.java b/extensions/amazon-lambda/event-server/src/main/java/io/quarkus/amazon/lambda/runtime/MockEventServer.java index cea6dfd37e2315..8c123ace9abaa6 100644 --- a/extensions/amazon-lambda/event-server/src/main/java/io/quarkus/amazon/lambda/runtime/MockEventServer.java +++ b/extensions/amazon-lambda/event-server/src/main/java/io/quarkus/amazon/lambda/runtime/MockEventServer.java @@ -5,6 +5,7 @@ import java.util.UUID; import java.util.concurrent.BlockingQueue; import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.ExecutionException; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.LinkedBlockingQueue; @@ -24,21 +25,6 @@ public class MockEventServer implements Closeable { protected static final Logger log = Logger.getLogger(MockEventServer.class); public static final int DEFAULT_PORT = 8081; - public void start() { - int port = DEFAULT_PORT; - start(port); - } - - public void start(int port) { - this.port = port; - vertx = Vertx.vertx(); - httpServer = vertx.createHttpServer(); - router = Router.router(vertx); - setupRoutes(); - httpServer.requestHandler(router).listen(port).result(); - log.info("Mock Lambda Event Server Started"); - } - private Vertx vertx; private int port; protected HttpServer httpServer; @@ -55,6 +41,25 @@ public MockEventServer() { queue = new LinkedBlockingQueue<>(); } + public void start() { + int port = DEFAULT_PORT; + start(port); + } + + public void start(int port) { + this.port = port; + vertx = Vertx.vertx(); + httpServer = vertx.createHttpServer(); + router = Router.router(vertx); + setupRoutes(); + try { + httpServer.requestHandler(router).listen(port).toCompletionStage().toCompletableFuture().get(); + } catch (InterruptedException | ExecutionException e) { + throw new RuntimeException(e); + } + log.info("Mock Lambda Event Server Started"); + } + public HttpServer getHttpServer() { return httpServer; } @@ -233,8 +238,18 @@ public void processError(RoutingContext ctx, RoutingContext pending, Buffer buff @Override public void close() throws IOException { log.info("Stopping Mock Lambda Event Server"); - httpServer.close().result(); - vertx.close().result(); - blockingPool.shutdown(); + try { + httpServer.close().toCompletionStage().toCompletableFuture().get(); + } catch (InterruptedException | ExecutionException e) { + throw new RuntimeException(e); + } finally { + try { + vertx.close().toCompletionStage().toCompletableFuture().get(); + } catch (InterruptedException | ExecutionException e) { + throw new RuntimeException(e); + } finally { + blockingPool.shutdown(); + } + } } } diff --git a/extensions/amazon-lambda/runtime/src/main/java/io/quarkus/amazon/lambda/runtime/AmazonLambdaRecorder.java b/extensions/amazon-lambda/runtime/src/main/java/io/quarkus/amazon/lambda/runtime/AmazonLambdaRecorder.java index 025873490e4334..6fdc74a0b6bab8 100644 --- a/extensions/amazon-lambda/runtime/src/main/java/io/quarkus/amazon/lambda/runtime/AmazonLambdaRecorder.java +++ b/extensions/amazon-lambda/runtime/src/main/java/io/quarkus/amazon/lambda/runtime/AmazonLambdaRecorder.java @@ -17,6 +17,7 @@ import io.quarkus.amazon.lambda.runtime.handlers.S3EventInputReader; import io.quarkus.arc.runtime.BeanContainer; +import io.quarkus.runtime.LaunchMode; import io.quarkus.runtime.ShutdownContext; import io.quarkus.runtime.annotations.Recorder; @@ -145,9 +146,9 @@ public void chooseHandlerClass(List>> uname } @SuppressWarnings("rawtypes") - public void startPollLoop(ShutdownContext context) { + public void startPollLoop(ShutdownContext context, LaunchMode launchMode) { AbstractLambdaPollLoop loop = new AbstractLambdaPollLoop(AmazonLambdaMapperRecorder.objectMapper, - AmazonLambdaMapperRecorder.cognitoIdReader, AmazonLambdaMapperRecorder.clientCtxReader) { + AmazonLambdaMapperRecorder.cognitoIdReader, AmazonLambdaMapperRecorder.clientCtxReader, launchMode) { @Override protected Object processRequest(Object input, AmazonLambdaContext context) throws Exception { diff --git a/extensions/funqy/funqy-amazon-lambda/deployment/src/main/java/io/quarkus/funqy/deployment/bindings/FunqyLambdaBuildStep.java b/extensions/funqy/funqy-amazon-lambda/deployment/src/main/java/io/quarkus/funqy/deployment/bindings/FunqyLambdaBuildStep.java index 82887800d171c2..6a8bcbdc213d9c 100644 --- a/extensions/funqy/funqy-amazon-lambda/deployment/src/main/java/io/quarkus/funqy/deployment/bindings/FunqyLambdaBuildStep.java +++ b/extensions/funqy/funqy-amazon-lambda/deployment/src/main/java/io/quarkus/funqy/deployment/bindings/FunqyLambdaBuildStep.java @@ -59,9 +59,10 @@ public RuntimeComplete choose(FunqyConfig config, FunqyLambdaBindingRecorder rec public void startPoolLoop(FunqyLambdaBindingRecorder recorder, RuntimeComplete ignored, ShutdownContextBuildItem shutdownContextBuildItem, + LaunchModeBuildItem launchModeBuildItem, List orderServicesFirst // try to order this after service recorders ) { - recorder.startPollLoop(shutdownContextBuildItem); + recorder.startPollLoop(shutdownContextBuildItem, launchModeBuildItem.getLaunchMode()); } @BuildStep @@ -73,7 +74,7 @@ public void startPoolLoopDevOrTest(RuntimeComplete ignored, LaunchModeBuildItem launchModeBuildItem) { LaunchMode mode = launchModeBuildItem.getLaunchMode(); if (mode.isDevOrTest()) { - recorder.startPollLoop(shutdownContextBuildItem); + recorder.startPollLoop(shutdownContextBuildItem, mode); } } } diff --git a/extensions/funqy/funqy-amazon-lambda/runtime/src/main/java/io/quarkus/funqy/lambda/FunqyLambdaBindingRecorder.java b/extensions/funqy/funqy-amazon-lambda/runtime/src/main/java/io/quarkus/funqy/lambda/FunqyLambdaBindingRecorder.java index 03ac85133e80cd..d2726940abd858 100644 --- a/extensions/funqy/funqy-amazon-lambda/runtime/src/main/java/io/quarkus/funqy/lambda/FunqyLambdaBindingRecorder.java +++ b/extensions/funqy/funqy-amazon-lambda/runtime/src/main/java/io/quarkus/funqy/lambda/FunqyLambdaBindingRecorder.java @@ -27,6 +27,7 @@ import io.quarkus.funqy.runtime.FunqyConfig; import io.quarkus.funqy.runtime.FunqyServerResponse; import io.quarkus.funqy.runtime.RequestContextImpl; +import io.quarkus.runtime.LaunchMode; import io.quarkus.runtime.ShutdownContext; import io.quarkus.runtime.annotations.Recorder; @@ -107,9 +108,9 @@ public static void handle(InputStream inputStream, OutputStream outputStream, Co } @SuppressWarnings("rawtypes") - public void startPollLoop(ShutdownContext context) { + public void startPollLoop(ShutdownContext context, LaunchMode launchMode) { AbstractLambdaPollLoop loop = new AbstractLambdaPollLoop(AmazonLambdaMapperRecorder.objectMapper, - AmazonLambdaMapperRecorder.cognitoIdReader, AmazonLambdaMapperRecorder.clientCtxReader) { + AmazonLambdaMapperRecorder.cognitoIdReader, AmazonLambdaMapperRecorder.clientCtxReader, launchMode) { @Override protected Object processRequest(Object input, AmazonLambdaContext context) throws Exception { diff --git a/extensions/vertx-http/deployment/src/main/java/io/quarkus/vertx/http/deployment/devmode/tests/TestsProcessor.java b/extensions/vertx-http/deployment/src/main/java/io/quarkus/vertx/http/deployment/devmode/tests/TestsProcessor.java index 8b58de97ce3ff5..04d51e13b7acad 100644 --- a/extensions/vertx-http/deployment/src/main/java/io/quarkus/vertx/http/deployment/devmode/tests/TestsProcessor.java +++ b/extensions/vertx-http/deployment/src/main/java/io/quarkus/vertx/http/deployment/devmode/tests/TestsProcessor.java @@ -58,7 +58,7 @@ public void setupTestRoutes( // Add continuous testing routeBuildItemBuildProducer.produce(nonApplicationRootPathBuildItem.routeBuilder() .route("dev/test") - .handler(recorder.continousTestHandler(shutdownContextBuildItem)) + .handler(recorder.continuousTestHandler(shutdownContextBuildItem)) .build()); } diff --git a/extensions/vertx-http/runtime/src/main/java/io/quarkus/vertx/http/runtime/devmode/DevConsoleRecorder.java b/extensions/vertx-http/runtime/src/main/java/io/quarkus/vertx/http/runtime/devmode/DevConsoleRecorder.java index 8b2e5cb4ee7558..57b06b28fc00d2 100644 --- a/extensions/vertx-http/runtime/src/main/java/io/quarkus/vertx/http/runtime/devmode/DevConsoleRecorder.java +++ b/extensions/vertx-http/runtime/src/main/java/io/quarkus/vertx/http/runtime/devmode/DevConsoleRecorder.java @@ -45,7 +45,7 @@ public Handler devConsoleHandler(String devConsoleFinalDestinati return new DevConsoleStaticHandler(devConsoleFinalDestination); } - public Handler continousTestHandler(ShutdownContext context) { + public Handler continuousTestHandler(ShutdownContext context) { ContinuousTestWebSocketHandler handler = new ContinuousTestWebSocketHandler(); ContinuousTestingSharedStateManager.addStateListener(handler);