Skip to content

Commit

Permalink
Fix lambda continuous testing failure
Browse files Browse the repository at this point in the history
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.
  • Loading branch information
stuartwdouglas committed Sep 10, 2021
1 parent 13b65be commit 6f32eca
Show file tree
Hide file tree
Showing 12 changed files with 84 additions and 52 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand Down Expand Up @@ -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)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -51,7 +51,7 @@ private boolean legacyTestingEnabled() {
public void startEventServer(LaunchModeBuildItem launchMode,
LambdaConfig config,
Optional<EventServerOverrideBuildItem> override,
BuildProducer<DevServicesNativeConfigResultBuildItem> devServicePropertiesProducer,
BuildProducer<DevServicesConfigResultBuildItem> devServicePropertiesProducer,
BuildProducer<ServiceStartBuildItem> serviceStartBuildItemBuildProducer) throws Exception {
if (!launchMode.getLaunchMode().isDevOrTest())
return;
Expand All @@ -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 {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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();
Expand All @@ -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.
Expand Down Expand Up @@ -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.
Expand Down Expand Up @@ -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()));
Expand All @@ -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();
Expand All @@ -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);
Expand Down Expand Up @@ -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);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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.
*/
Expand Down Expand Up @@ -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");
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -240,9 +240,10 @@ public void recordHandlerClass(List<AmazonLambdaBuildItem> lambdas,
@Record(value = ExecutionTime.RUNTIME_INIT)
void startPoolLoop(AmazonLambdaRecorder recorder,
ShutdownContextBuildItem shutdownContextBuildItem,
LaunchModeBuildItem launchModeBuildItem,
List<ServiceStartBuildItem> orderServicesFirst // try to order this after service recorders
) {
recorder.startPollLoop(shutdownContextBuildItem);
recorder.startPollLoop(shutdownContextBuildItem, launchModeBuildItem.getLaunchMode());
}

@BuildStep
Expand All @@ -253,7 +254,7 @@ void startPoolLoopDevOrTest(AmazonLambdaRecorder recorder,
LaunchModeBuildItem launchModeBuildItem) {
LaunchMode mode = launchModeBuildItem.getLaunchMode();
if (mode.isDevOrTest()) {
recorder.startPollLoop(shutdownContextBuildItem);
recorder.startPollLoop(shutdownContextBuildItem, mode);
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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;
Expand All @@ -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;
}
Expand Down Expand Up @@ -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();
}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand Down Expand Up @@ -145,9 +146,9 @@ public void chooseHandlerClass(List<Class<? extends RequestHandler<?, ?>>> 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 {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -59,9 +59,10 @@ public RuntimeComplete choose(FunqyConfig config, FunqyLambdaBindingRecorder rec
public void startPoolLoop(FunqyLambdaBindingRecorder recorder,
RuntimeComplete ignored,
ShutdownContextBuildItem shutdownContextBuildItem,
LaunchModeBuildItem launchModeBuildItem,
List<ServiceStartBuildItem> orderServicesFirst // try to order this after service recorders
) {
recorder.startPollLoop(shutdownContextBuildItem);
recorder.startPollLoop(shutdownContextBuildItem, launchModeBuildItem.getLaunchMode());
}

@BuildStep
Expand All @@ -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);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand Down Expand Up @@ -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 {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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());
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ public Handler<RoutingContext> devConsoleHandler(String devConsoleFinalDestinati
return new DevConsoleStaticHandler(devConsoleFinalDestination);
}

public Handler<RoutingContext> continousTestHandler(ShutdownContext context) {
public Handler<RoutingContext> continuousTestHandler(ShutdownContext context) {

ContinuousTestWebSocketHandler handler = new ContinuousTestWebSocketHandler();
ContinuousTestingSharedStateManager.addStateListener(handler);
Expand Down

0 comments on commit 6f32eca

Please sign in to comment.