Skip to content

Commit a266377

Browse files
committed
working example
1 parent d96886e commit a266377

File tree

9 files changed

+215
-73
lines changed

9 files changed

+215
-73
lines changed

.mvn/wrapper/maven-wrapper.jar

46.7 KB
Binary file not shown.

.mvn/wrapper/maven-wrapper.properties

+1
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
distributionUrl=https://repo1.maven.org/maven2/org/apache/maven/apache-maven/3.5.0/apache-maven-3.5.0-bin.zip

README.md

+64-6
Original file line numberDiff line numberDiff line change
@@ -2,15 +2,15 @@
22

33
### Custom Vert.x Native for AWS Lambda
44

5-
*Disclaimer - This project should be considered a POC and has not been tested or verified for production use.
5+
*Disclaimer - This project should be considered a POC and has not been tested or verified for production use.
66
If you decided to run this on production systems you do so at your own risk.*
77

88
### Building this Runtime
99

1010

11-
#### Prerequisites
11+
#### Prerequisites
1212

13-
Make sure you have the following installed on your build machine before getting started.
13+
Make sure you have the following installed on your build machine before getting started.
1414

1515
* GraalVM
1616
* AWS CLI
@@ -38,17 +38,75 @@ Add the following commands to the ```bootstrap```
3838
/opt/target/lambda
3939
```
4040

41-
Note that the path we're using in our shell script is ```/opt```. When you create a Lambda Layer, as we'll do shortly, AWS Lambda copies all the runtime files to the ```/opt``` directory. This directory is effectively the home directory for our custom runtime.
41+
Note that the path we're using in our shell script is ```/opt```. When you create a Lambda Layer, as we'll do shortly, AWS Lambda copies all the runtime files to the ```/opt``` directory. This directory is effectively the home directory for our custom runtime.
4242

4343
##### Make bootstrap executable
4444
```
45-
$ chmod +x bootstrap
45+
$ chmod +x bootstrap
4646
```
4747

4848
##### Create a deployment package
4949

50-
In the root of the folder containing our ```bootstrap``` and ```target/lambda``` files, create a zip archive containing the artifacts.
50+
In the root of the folder containing our ```bootstrap``` and ```target/lambda``` files, create a zip archive containing the artifacts.
5151

5252
```
5353
$ zip -r function.zip bootstrap target/lambda
5454
```
55+
56+
### Deploying to AWS Lambda
57+
58+
#### Create a lambda role
59+
60+
```
61+
aws iam create-role \
62+
--role-name lambda-role \
63+
--path "/service-role/" \
64+
--assume-role-policy-document file:///tmp/trust-policy.json
65+
```
66+
67+
Where the file `/tmp/trust-policy.json` contains:
68+
69+
```json
70+
{
71+
"Version": "2012-10-17",
72+
"Statement": [
73+
{
74+
"Effect": "Allow",
75+
"Principal": {
76+
"Service": "lambda.amazonaws.com"
77+
},
78+
"Action": "sts:AssumeRole"
79+
}
80+
]
81+
}
82+
```
83+
84+
#### Publish a lambda layer
85+
86+
```
87+
aws lambda publish-layer-version \
88+
--layer-name vertx-native-example \
89+
--zip-file fileb://function.zip
90+
```
91+
92+
#### Create a function
93+
94+
```
95+
aws lambda delete-function --function-name vertxNativeTester
96+
97+
aws lambda create-function --function-name vertxNativeTester \
98+
--zip-file fileb://function.zip --handler lambda.EchoLambda --runtime provided \
99+
--role arn:aws:iam::985727241951:role/service-role/lambda-role
100+
```
101+
102+
#### Link the layer to the function
103+
104+
```
105+
aws lambda update-function-configuration --function-name vertxNativeTester --layers arn:aws:lambda:eu-central-1:985727241951:layer:vertx-native-example:8
106+
```
107+
108+
#### Test it
109+
110+
```
111+
aws lambda invoke --function-name vertxNativeTester --payload '{"message":"Hello World"}' --log-type Tail response.txt | grep "LogResult"| awk -F'"' '{print $4}' | base64 --decode
112+
```

src/main/java/lambda/EchoLambda.java

+33
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
/*
2+
* Copyright 2019 Paulo Lopes.
3+
*
4+
* All rights reserved. This program and the accompanying materials
5+
* are made available under the terms of the Eclipse Public License v1.0
6+
* and Apache License v2.0 which accompanies this distribution.
7+
*
8+
* The Eclipse Public License is available at
9+
* http://www.eclipse.org/legal/epl-v10.html
10+
*
11+
* The Apache License v2.0 is available at
12+
* http://www.opensource.org/licenses/apache2.0.php
13+
*
14+
* You may elect to redistribute this code under either of these licenses.
15+
*/
16+
package lambda;
17+
18+
import io.vertx.core.Future;
19+
import io.vertx.core.Vertx;
20+
import io.vertx.core.buffer.Buffer;
21+
import io.vertx.ext.web.client.HttpResponse;
22+
import vertx.lambda.Lambda;
23+
24+
/**
25+
* This is a simple example of an echo Lambda.
26+
*/
27+
public class EchoLambda implements Lambda {
28+
29+
@Override
30+
public Future<Buffer> call(Vertx vertx, HttpResponse<Buffer> request) {
31+
return Future.succeededFuture(request.body());
32+
}
33+
}

src/main/java/lambda/MyLambda.java

-13
This file was deleted.
+29-1
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,37 @@
1+
/*
2+
* Copyright 2019 Paulo Lopes.
3+
*
4+
* All rights reserved. This program and the accompanying materials
5+
* are made available under the terms of the Eclipse Public License v1.0
6+
* and Apache License v2.0 which accompanies this distribution.
7+
*
8+
* The Eclipse Public License is available at
9+
* http://www.eclipse.org/legal/epl-v10.html
10+
*
11+
* The Apache License v2.0 is available at
12+
* http://www.opensource.org/licenses/apache2.0.php
13+
*
14+
* You may elect to redistribute this code under either of these licenses.
15+
*/
116
package vertx.lambda;
217

318
import io.vertx.core.Future;
19+
import io.vertx.core.Vertx;
420
import io.vertx.core.buffer.Buffer;
21+
import io.vertx.ext.web.client.HttpResponse;
522

23+
/**
24+
* The functional interface that represents a lambda
25+
*/
26+
@FunctionalInterface
627
public interface Lambda {
728

8-
Future<Buffer> call(Buffer request);
29+
/**
30+
* Responses are asynchronous.
31+
*
32+
* @param vertx the vertx instance if needed for more IO
33+
* @param request the function request
34+
* @return return a future with the buffer to be returned.
35+
*/
36+
Future<Buffer> call(Vertx vertx, HttpResponse<Buffer> request);
937
}

src/main/java/vertx/lambda/LambdaBootstrap.java

+83-49
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,25 @@
1+
/*
2+
* Copyright 2019 Paulo Lopes.
3+
*
4+
* All rights reserved. This program and the accompanying materials
5+
* are made available under the terms of the Eclipse Public License v1.0
6+
* and Apache License v2.0 which accompanies this distribution.
7+
*
8+
* The Eclipse Public License is available at
9+
* http://www.eclipse.org/legal/epl-v10.html
10+
*
11+
* The Apache License v2.0 is available at
12+
* http://www.opensource.org/licenses/apache2.0.php
13+
*
14+
* You may elect to redistribute this code under either of these licenses.
15+
*/
116
package vertx.lambda;
217

318
import io.vertx.core.Vertx;
419
import io.vertx.core.buffer.Buffer;
520
import io.vertx.core.json.JsonObject;
621
import io.vertx.ext.web.client.HttpResponse;
722
import io.vertx.ext.web.client.WebClient;
8-
import io.vertx.core.AbstractVerticle;
9-
import io.vertx.core.Handler;
1023

1124
import static java.lang.System.getenv;
1225

@@ -15,17 +28,25 @@
1528
import java.util.Map;
1629
import java.util.ServiceLoader;
1730

31+
/**
32+
* Main entrypoint for the application.
33+
*/
1834
public class LambdaBootstrap {
1935

2036
private static final String LAMBDA_VERSION_DATE = "2018-06-01";
21-
private static final String LAMBDA_RUNTIME_URL_TEMPLATE = "http://{0}/{1}/runtime/invocation/next";
22-
private static final String LAMBDA_INVOCATION_URL_TEMPLATE = "http://{0}/{1}/runtime/invocation/{2}/response";
23-
private static final String LAMBDA_INIT_ERROR_URL_TEMPLATE = "http://{0}/{1}/runtime/init/error";
24-
private static final String LAMBDA_ERROR_URL_TEMPLATE = "http://{0}/{1}/runtime/invocation/{2}/error";
37+
38+
private static final String LAMBDA_RUNTIME_TEMPLATE = "/{0}/runtime/invocation/next";
39+
private static final String LAMBDA_INVOCATION_TEMPLATE = "/{0}/runtime/invocation/{1}/response";
40+
private static final String LAMBDA_INIT_ERROR_TEMPLATE = "/{0}/runtime/init/error";
41+
private static final String LAMBDA_ERROR_TEMPLATE = "/{0}/runtime/invocation/{1}/error";
2542

2643
private static final Map<String, Lambda> HANDLERS = new HashMap<>();
2744

2845
static {
46+
System.setProperty("vertx.disableDnsResolver", "true");
47+
System.setProperty("vertx.cacheDirBase", "/tmp/vertx-cache");
48+
System.setProperty("java.net.preferIPv4Stack", "true");
49+
2950
// load all handlers available, if this becomes a performance
3051
ServiceLoader<Lambda> serviceLoader = ServiceLoader.load(Lambda.class);
3152
for (Lambda fn : serviceLoader) {
@@ -34,64 +55,78 @@ public class LambdaBootstrap {
3455
}
3556

3657
public static void main(String[] args) {
37-
new LambdaBootstrap(Vertx.vertx());
58+
try {
59+
new LambdaBootstrap(Vertx.vertx());
60+
} catch (RuntimeException e) {
61+
e.printStackTrace();
62+
System.exit(1);
63+
}
3864
}
3965

4066
private final WebClient client;
4167

42-
private final String runtimeApi;
4368
private final Lambda fn;
4469

70+
private final String host;
71+
private final int port;
72+
4573
private LambdaBootstrap(Vertx vertx) {
4674
// create an WebClient
4775
this.client = WebClient.create(vertx);
4876

49-
this.runtimeApi = getenv("AWS_LAMBDA_RUNTIME_API");
77+
String runtimeApi = getenv("AWS_LAMBDA_RUNTIME_API");
78+
79+
int sep = runtimeApi.indexOf(':');
80+
if (sep != -1) {
81+
host = runtimeApi.substring(0, sep);
82+
port = Integer.parseInt(runtimeApi.substring(sep + 1));
83+
} else {
84+
host = runtimeApi;
85+
port = 80;
86+
}
87+
5088
// Get the handler class and method name from the Lambda Configuration in the format of <fqcn>
5189
this.fn = HANDLERS.get(getenv("_HANDLER"));
52-
final String runtimeUrl = MessageFormat.format(LAMBDA_RUNTIME_URL_TEMPLATE, runtimeApi, LAMBDA_VERSION_DATE);
90+
final String runtimeUrl = MessageFormat.format(LAMBDA_RUNTIME_TEMPLATE, LAMBDA_VERSION_DATE);
5391

5492
if (fn == null) {
5593
// Not much else to do handler can't be found.
56-
fail(MessageFormat.format(LAMBDA_INIT_ERROR_URL_TEMPLATE, runtimeApi, LAMBDA_VERSION_DATE), "Could not find handler method", "InitError");
57-
return;
58-
}
59-
60-
client.getAbs(runtimeUrl).send(getAbs -> {
61-
if (getAbs.succeeded()) {
62-
HttpResponse<Buffer> response = getAbs.result();
63-
64-
String requestId = response.getHeader("Lambda-Runtime-Aws-Request-Id");
65-
66-
try {
67-
// Invoke Handler Method
68-
fn.call(response.body())
69-
.setHandler(ar -> {
70-
if (ar.succeeded()) {
71-
// Post the results of Handler Invocation
72-
String invocationUrl = MessageFormat.format(LAMBDA_INVOCATION_URL_TEMPLATE, runtimeApi, LAMBDA_VERSION_DATE, requestId);
73-
success(invocationUrl, ar.result());
74-
} else {
75-
String initErrorUrl = MessageFormat.format(LAMBDA_ERROR_URL_TEMPLATE, runtimeApi, LAMBDA_VERSION_DATE, requestId);
76-
fail(initErrorUrl, "Invocation Error", "RuntimeError");
77-
ar.cause().printStackTrace();
78-
}
79-
});
80-
81-
} catch (Exception e) {
82-
String initErrorUrl = MessageFormat.format(LAMBDA_ERROR_URL_TEMPLATE, runtimeApi, LAMBDA_VERSION_DATE, requestId);
83-
fail(initErrorUrl, "Invocation Error", "RuntimeError");
84-
e.printStackTrace();
94+
fail(MessageFormat.format(LAMBDA_INIT_ERROR_TEMPLATE, LAMBDA_VERSION_DATE), "Could not find handler method", "InitError");
95+
} else {
96+
client.get(port, host, runtimeUrl).send(getAbs -> {
97+
if (getAbs.succeeded()) {
98+
HttpResponse<Buffer> response = getAbs.result();
99+
100+
String requestId = response.getHeader("Lambda-Runtime-Aws-Request-Id");
101+
102+
try {
103+
// Invoke Handler Method
104+
fn.call(vertx, response)
105+
.setHandler(ar -> {
106+
if (ar.succeeded()) {
107+
// Post the results of Handler Invocation
108+
String invocationUrl = MessageFormat.format(LAMBDA_INVOCATION_TEMPLATE, LAMBDA_VERSION_DATE, requestId);
109+
success(invocationUrl, ar.result());
110+
} else {
111+
String initErrorUrl = MessageFormat.format(LAMBDA_ERROR_TEMPLATE, LAMBDA_VERSION_DATE, requestId);
112+
fail(initErrorUrl, "Invocation Error", "RuntimeError");
113+
}
114+
});
115+
116+
} catch (Exception e) {
117+
String initErrorUrl = MessageFormat.format(LAMBDA_ERROR_TEMPLATE, LAMBDA_VERSION_DATE, requestId);
118+
fail(initErrorUrl, "Invocation Error", "RuntimeError");
119+
}
120+
} else {
121+
getAbs.cause().printStackTrace();
122+
System.exit(1);
85123
}
86-
} else {
87-
getAbs.cause().printStackTrace();
88-
}
89-
});
124+
});
125+
}
90126
}
91127

92-
private void success(String successUrl, Buffer result) {
93-
94-
client.postAbs(successUrl)
128+
private void success(String requestURI, Buffer result) {
129+
client.post(port, host, requestURI)
95130
.sendBuffer(result, ar -> {
96131
if (ar.succeeded()) {
97132
// we don't really care about the response
@@ -103,13 +138,12 @@ private void success(String successUrl, Buffer result) {
103138
});
104139
}
105140

106-
private void fail(String errorUrl, String errMsg, String errType) {
107-
141+
private void fail(String requestURI, String errMsg, String errType) {
108142
final JsonObject error = new JsonObject()
109143
.put("errorMessage", errMsg)
110144
.put("errorType", errType);
111145

112-
client.postAbs(errorUrl)
146+
client.post(port, host, requestURI)
113147
.sendJson(error, ar -> {
114148
if (ar.succeeded()) {
115149
// we don't really care about the response
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,11 @@
1-
Args = --enable-url-protocols=http \
2-
-H:+ReportUnsupportedElementsAtRuntime \
1+
Args = -H:+ReportUnsupportedElementsAtRuntime \
32
--allow-incomplete-classpath \
43
--rerun-class-initialization-at-runtime=io.netty.handler.codec.http2.Http2CodecUtil \
54
--delay-class-initialization-to-runtime=io.netty.handler.codec.http.HttpObjectEncoder,io.netty.handler.codec.http2.DefaultHttp2FrameWriter,io.netty.handler.codec.http.websocketx.WebSocket00FrameEncoder,io.netty.handler.ssl.JdkNpnApplicationProtocolNegotiator,io.netty.handler.ssl.ReferenceCountedOpenSslEngine,io.vertx.core.net.impl.transport.EpollTransport,io.vertx.core.net.impl.transport.KQueueTransport,io.netty.handler.ssl.ReferenceCountedOpenSslClientContext,io.netty.handler.ssl.ReferenceCountedOpenSslServerContext,io.netty.handler.ssl.ConscryptAlpnSslEngine,io.netty.handler.ssl.JettyNpnSslEngine \
65
-H:IncludeResources=(META-INF/vertx|META-INF/services|static|webroot|template)/.* \
76
-H:ReflectionConfigurationFiles=classes/${.}/reflection.json \
87
-H:Name=lambda
98

10-
JavaArgs = -Dvertx.disableDnsResolver=true
9+
JavaArgs = -Dvertx.disableDnsResolver=true \
10+
-Dvertx.cacheDirBase=/tmp/vertx-cache \
11+
-Djava.net.preferIPv4Stack=true
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
lambda.MyLambda
1+
lambda.EchoLambda

0 commit comments

Comments
 (0)