micronaut-projects / micronaut-aws

Projects specific to integrating Micronaut and Amazon Web Services (AWS)
Apache License 2.0
87 stars 80 forks source link

Lambda Native Image with Deserialization Error #1376

Open driverpt opened 2 years ago

driverpt commented 2 years ago

Expected Behavior

AWS Lambda API Gateway Proxy Native Lambda to start

Actual Behaviour

[main] INFO io.micronaut.function.aws.proxy.AbstractLambdaContainerHandler - Starting Lambda Container Handler
[main] INFO io.micronaut.context.env.DefaultEnvironment - Established active environments: [ec2, cloud, lambda, function]
io.micronaut.http.client.exceptions.HttpClientResponseException: Error decoding HTTP response body: No bean introspection available for type [class com.amazonaws.serverless.proxy.model.AwsProxyRequest]. Ensure the class is annotated with io.micronaut.core.annotation.Introspected

Steps To Reproduce

Dependencies:

    annotationProcessor("org.projectlombok:lombok")
    annotationProcessor("io.micronaut:micronaut-http-validation")
    annotationProcessor("io.micronaut.serde:micronaut-serde-processor")

    implementation("io.micronaut:micronaut-http-client")
    implementation("io.micronaut:micronaut-jackson-databind")
    implementation("io.micronaut:micronaut-runtime")
    implementation("io.micronaut.aws:micronaut-aws-sdk-v2")
    implementation("software.amazon.awssdk:s3") {
        exclude("software.amazon.awssdk", "apache-client")
        exclude("software.amazon.awssdk", "netty-nio-client")
    }
    implementation("software.amazon.awssdk:sso") {
        exclude("software.amazon.awssdk", "apache-client")
        exclude("software.amazon.awssdk", "netty-nio-client")
    }
    implementation("software.amazon.awssdk:url-connection-client")

    implementation("io.micronaut.reactor:micronaut-reactor")
    implementation("io.micronaut.reactor:micronaut-reactor-http-client")
    implementation("io.micronaut.serde:micronaut-serde-jackson")
    implementation("io.micronaut.tracing:micronaut-tracing-zipkin")
    implementation("jakarta.annotation:jakarta.annotation-api")

    implementation("io.micronaut.aws:micronaut-function-aws-api-proxy")

    compileOnly("org.projectlombok:lombok")

    runtimeOnly("org.slf4j:slf4j-simple")

    testImplementation("org.assertj:assertj-core")
    testImplementation("org.testcontainers:junit-jupiter")
    testImplementation("org.testcontainers:localstack")
    testImplementation("org.testcontainers:testcontainers")

    compileOnly("org.graalvm.nativeimage:svm")

    implementation("io.micronaut:micronaut-validation")

    testImplementation("io.micronaut.aws:micronaut-function-aws-test")
    testImplementation("org.hamcrest:hamcrest")
    testImplementation("org.mockito:mockito-core")

SAM Lambda Declaration:

  LambdaFunction:
    Type: AWS::Serverless::Function
    Metadata:
      BuildMethod: makefile
    Properties:
      FunctionName: "SomeFunctionName"
      VpcConfig:
         ...
      Environment:
        Variables:
           ....
      Handler: bootstrap
      Runtime: provided.al2

Makefile

build-LambdaFunction:
    $(GRADLE_CMD) buildNativeLambda
    unzip build/libs/function-0.1-lambda.zip -d $(ARTIFACTS_DIR)/

Using sam build

Environment Information

Example Application

No response

Version

Micronaut: 3.4.3

driverpt commented 2 years ago

@TypeHint missing for com.amazonaws.serverless.proxy.model.AwsProxyRequest ?

UPDATE: No, it's there

sdelamo commented 2 years ago

do you have a sample app?

driverpt commented 2 years ago

I'm creating it right now.

driverpt commented 2 years ago

@sdelamo , please check the Repo Here

https://github.com/driverpt/micronaut-aws-api-proxy-graalvm-error

To build just run

make build

And upload the Zip file to AWS Lambda or run locally using sam local invoke

Just tested it again:

START RequestId: 6044b2a5-1b2d-49f6-92b0-0ce5afc9e222 Version: $LATEST
11:35:31.036 [main] INFO  i.m.f.a.p.AbstractLambdaContainerHandler - Starting Lambda Container Handler
11:35:37.011 [main] INFO  i.m.context.env.DefaultEnvironment - Established active environments: [ec2, cloud, lambda, function]
io.micronaut.http.client.exceptions.HttpClientResponseException: Error decoding HTTP response body: No bean introspection available for type [class com.amazonaws.serverless.proxy.model.AwsProxyRequest]. Ensure the class is annotated with io.micronaut.core.annotation.Introspected
    at io.micronaut.http.client.netty.DefaultHttpClient$11.channelReadInstrumented(DefaultHttpClient.java:2546)
    at io.micronaut.http.client.netty.DefaultHttpClient$11.channelReadInstrumented(DefaultHttpClient.java:2389)
    at io.micronaut.http.client.netty.DefaultHttpClient$SimpleChannelInboundHandlerInstrumented.channelRead0(DefaultHttpClient.java:3145)
    at io.netty.channel.SimpleChannelInboundHandler.channelRead(SimpleChannelInboundHandler.java:99)

Let me know if i can help or brainstorm the solution.

driverpt commented 2 years ago

cc @msailes

driverpt commented 2 years ago

Forgot to mention, to Test, just invoke the Lambda with an example API Gateway V1 Payload in the UI.

image

driverpt commented 2 years ago

Hey folks.

Any update on this?

msailes commented 2 years ago

Your SAM template has the handler as bootstrap I think it should be io.micronaut.function.aws.proxy.MicronautLambdaHandler

https://github.com/micronaut-projects/micronaut-aws/blob/master/function-aws-api-proxy/src/main/java/io/micronaut/function/aws/proxy/MicronautLambdaContainerHandler.java

Has @TypeHint for com.amazonaws.serverless.proxy.model.AwsProxyRequest

driverpt commented 2 years ago

Hello @msailes ,

Having the same result:

image image

Stacktrace:


io.micronaut.http.client.exceptions.HttpClientResponseException: Error decoding HTTP response body: No bean introspection available for type [class com.amazonaws.serverless.proxy.model.AwsProxyRequest]. Ensure the class is annotated with io.micronaut.core.annotation.Introspected
--
at io.micronaut.http.client.netty.DefaultHttpClient$11.channelReadInstrumented(DefaultHttpClient.java:2546)
at io.micronaut.http.client.netty.DefaultHttpClient$11.channelReadInstrumented(DefaultHttpClient.java:2389)
at io.micronaut.http.client.netty.DefaultHttpClient$SimpleChannelInboundHandlerInstrumented.channelRead0(DefaultHttpClient.java:3145)
at io.netty.channel.SimpleChannelInboundHandler.channelRead(SimpleChannelInboundHandler.java:99)
at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:379)
at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:365)
at io.netty.channel.AbstractChannelHandlerContext.fireChannelRead(AbstractChannelHandlerContext.java:357)
at io.micronaut.http.netty.stream.HttpStreamsHandler.channelRead(HttpStreamsHandler.java:215)
at io.micronaut.http.netty.stream.HttpStreamsClientHandler.channelRead(HttpStreamsClientHandler.java:180)
at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:379)
at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:365)
at io.netty.channel.AbstractChannelHandlerContext.fireChannelRead(AbstractChannelHandlerContext.java:357)
at io.netty.handler.codec.MessageToMessageDecoder.channelRead(MessageToMessageDecoder.java:103)
at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:379)
at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:365)
at io.netty.channel.AbstractChannelHandlerContext.fireChannelRead(AbstractChannelHandlerContext.java:357)
at io.netty.handler.codec.MessageToMessageDecoder.channelRead(MessageToMessageDecoder.java:103)
at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:379)
at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:365)
at io.netty.channel.AbstractChannelHandlerContext.fireChannelRead(AbstractChannelHandlerContext.java:357)
at io.netty.channel.CombinedChannelDuplexHandler$DelegatingChannelHandlerContext.fireChannelRead(CombinedChannelDuplexHandler.java:436)
at io.netty.handler.codec.ByteToMessageDecoder.fireChannelRead(ByteToMessageDecoder.java:327)
at io.netty.handler.codec.ByteToMessageDecoder.channelRead(ByteToMessageDecoder.java:299)
at io.netty.channel.CombinedChannelDuplexHandler.channelRead(CombinedChannelDuplexHandler.java:251)
at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:379)
at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:365)
at io.netty.channel.AbstractChannelHandlerContext.fireChannelRead(AbstractChannelHandlerContext.java:357)
at io.netty.channel.DefaultChannelPipeline$HeadContext.channelRead(DefaultChannelPipeline.java:1410)
at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:379)
at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:365)
at io.netty.channel.DefaultChannelPipeline.fireChannelRead(DefaultChannelPipeline.java:919)
at io.netty.channel.nio.AbstractNioByteChannel$NioByteUnsafe.read(AbstractNioByteChannel.java:166)
at io.netty.channel.nio.NioEventLoop.processSelectedKey(NioEventLoop.java:722)
at io.netty.channel.nio.NioEventLoop.processSelectedKeysOptimized(NioEventLoop.java:658)
at io.netty.channel.nio.NioEventLoop.processSelectedKeys(NioEventLoop.java:584)
at io.netty.channel.nio.NioEventLoop.run(NioEventLoop.java:496)
at io.netty.util.concurrent.SingleThreadEventExecutor$4.run(SingleThreadEventExecutor.java:986)
at io.netty.util.internal.ThreadExecutorMap$2.run(ThreadExecutorMap.java:74)
at io.netty.util.concurrent.FastThreadLocalRunnable.run(FastThreadLocalRunnable.java:30)
at java.lang.Thread.run(Thread.java:829)
at com.oracle.svm.core.thread.JavaThreads.threadStartRoutine(JavaThreads.java:597)
at com.oracle.svm.core.posix.thread.PosixJavaThreads.pthreadStartRoutine(PosixJavaThreads.java:194)
Suppressed: java.lang.Exception: #block terminated with an error
at reactor.core.publisher.BlockingSingleSubscriber.blockingGet(BlockingSingleSubscriber.java:99)
at reactor.core.publisher.Flux.blockFirst(Flux.java:2600)
at io.micronaut.http.client.netty.DefaultHttpClient$3.exchange(DefaultHttpClient.java:615)
at io.micronaut.http.client.BlockingHttpClient.exchange(BlockingHttpClient.java:77)
at io.micronaut.function.aws.runtime.AbstractMicronautLambdaRuntime.startRuntimeApiEventLoop(AbstractMicronautLambdaRuntime.java:338)
at io.micronaut.function.aws.runtime.AbstractMicronautLambdaRuntime.run(AbstractMicronautLambdaRuntime.java:137)
at io.micronaut.function.aws.runtime.MicronautLambdaRuntime.main(MicronautLambdaRuntime.java:50)
Caused by: io.micronaut.core.beans.exceptions.IntrospectionException: No bean introspection available for type [class com.amazonaws.serverless.proxy.model.AwsProxyRequest]. Ensure the class is annotated with io.micronaut.core.annotation.Introspected
at io.micronaut.core.beans.BeanIntrospector.lambda$getIntrospection$3(BeanIntrospector.java:117)
at java.util.Optional.orElseThrow(Optional.java:408)
at io.micronaut.core.beans.BeanIntrospector.getIntrospection(BeanIntrospector.java:117)
at io.micronaut.serde.support.DefaultSerdeIntrospections.lambda$getDeserializableIntrospection$1(DefaultSerdeIntrospections.java:129)
at java.util.Optional.orElseGet(Optional.java:369)
at io.micronaut.serde.support.DefaultSerdeIntrospections.getDeserializableIntrospection(DefaultSerdeIntrospections.java:120)
at io.micronaut.serde.support.deserializers.ObjectDeserializer.createDeserBean(ObjectDeserializer.java:76)
at io.micronaut.serde.support.deserializers.ObjectDeserializer.getDeserializableBean(ObjectDeserializer.java:68)
at io.micronaut.serde.support.deserializers.ObjectDeserializer.createSpecific(ObjectDeserializer.java:56)
at io.micronaut.serde.jackson.JacksonJsonMapper.readValue0(JacksonJsonMapper.java:122)
at io.micronaut.serde.jackson.JacksonJsonMapper.readValue(JacksonJsonMapper.java:116)
at io.micronaut.serde.jackson.JacksonJsonMapper.readValue(JacksonJsonMapper.java:167)
at io.micronaut.json.codec.MapperMediaTypeCodec.decode(MapperMediaTypeCodec.java:189)
at io.micronaut.http.client.netty.FullNettyClientHttpResponse.convertByteBuf(FullNettyClientHttpResponse.java:279)
at io.micronaut.http.client.netty.FullNettyClientHttpResponse.lambda$getBody$1(FullNettyClientHttpResponse.java:217)
at java.util.HashMap.computeIfAbsent(HashMap.java:1134)
at io.micronaut.http.client.netty.FullNettyClientHttpResponse.getBody(FullNettyClientHttpResponse.java:191)
at io.micronaut.http.client.netty.FullNettyClientHttpResponse.<init>(FullNettyClientHttpResponse.java:110)
at io.micronaut.http.client.netty.DefaultHttpClient$11.channelReadInstrumented(DefaultHttpClient.java:2466)
... 41 more
Request loop failed with: Error decoding HTTP response body: No bean introspection available for type [class com.amazonaws.serverless.proxy.model.AwsProxyRequest]. Ensure the class is annotated with io.micronaut.core.annotation.Introspected
sdelamo commented 2 years ago

I've not had the time yet to check your code.

You should not need to do this but you can generate @Introspected metadata information about a class.

https://micronaut-projects.github.io/micronaut-core/latest/guide/#introspection:

If the class to introspect is already compiled and not under your control, an alternative option is to define a > configuration class with the classes member of the @Introspected annotation set.

You could add @Introspected(classes = com.amazonaws.serverless.proxy.model.AwsProxyRequest.class) to one of your classes.

Let me know if it works.

I hope to be able to check your example soon.

driverpt commented 2 years ago

Hello @sdelamo , thanks for the reply.

I'm still having the same issue with the recommended change

UPDATE: I think this relates to #1328

driverpt commented 2 years ago

Updated Micronaut to 3.5.0 and added the following annotations:

@Introspected(classes = com.amazonaws.serverless.proxy.model.AwsProxyRequest.class)
@SerdeImport(AwsProxyRequest.class)

Now i'm getting a different error:

Request loop failed with: Error decoding HTTP response body: No bean introspection available for type [class com.amazonaws.serverless.proxy.model.AwsProxyRequestContext]. Ensure the class is annotated with io.micronaut.core.annotation.Introspected

I also noticed that a PR already has the necessary code to get this to work:

https://github.com/micronaut-projects/micronaut-aws/pull/1323/files#diff-dd8cf116b1d2f8e327fcac6c6f62b5464b6d52d16c6a8563be369b7c6fdea1a1R120-R131

Do we have an ETA for this PR to be merged?

Thanks