Skip to content

Commit e477257

Browse files
authored
Add FaaS (AWS Lambda) test app (mongodb#1183)
JAVA-4758
1 parent 80ff9be commit e477257

File tree

10 files changed

+394
-3
lines changed

10 files changed

+394
-3
lines changed

.evergreen/.evg.yml

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1512,6 +1512,23 @@ tasks:
15121512
- func: "run perf tests"
15131513
- func: "send dashboard data"
15141514

1515+
- name: "test-aws-lambda-deployed"
1516+
commands:
1517+
- command: ec2.assume_role
1518+
params:
1519+
role_arn: ${LAMBDA_AWS_ROLE_ARN}
1520+
duration_seconds: 3600
1521+
- command: subprocess.exec
1522+
params:
1523+
working_dir: src
1524+
binary: bash
1525+
add_expansions_to_env: true
1526+
args:
1527+
- .evergreen/run-deployed-lambda-aws-tests.sh
1528+
env:
1529+
TEST_LAMBDA_DIRECTORY: ${PROJECT_DIRECTORY}/driver-lambda/
1530+
AWS_REGION: us-east-1
1531+
15151532
- name: "mmapv1-storage-test"
15161533
commands:
15171534
- func: "bootstrap mongo-orchestration"
@@ -1965,6 +1982,32 @@ task_groups:
19651982
$DRIVERS_TOOLS/.evergreen/csfle/azurekms/delete-vm.sh
19661983
tasks:
19671984
- testazurekms-task
1985+
- name: test_atlas_task_group
1986+
setup_group:
1987+
- func: fetch source
1988+
- func: prepare resources
1989+
- command: subprocess.exec
1990+
params:
1991+
working_dir: src
1992+
binary: bash
1993+
add_expansions_to_env: true
1994+
args:
1995+
- ${DRIVERS_TOOLS}/.evergreen/atlas/setup-atlas-cluster.sh
1996+
- command: expansions.update
1997+
params:
1998+
file: src/atlas-expansion.yml
1999+
teardown_group:
2000+
- command: subprocess.exec
2001+
params:
2002+
working_dir: src
2003+
binary: bash
2004+
add_expansions_to_env: true
2005+
args:
2006+
- ${DRIVERS_TOOLS}/.evergreen/atlas/teardown-atlas-cluster.sh
2007+
setup_group_can_fail_task: true
2008+
setup_group_timeout_secs: 1800
2009+
tasks:
2010+
- test-aws-lambda-deployed
19682011

19692012
buildvariants:
19702013

@@ -2096,6 +2139,12 @@ buildvariants:
20962139
tasks:
20972140
- name: "perf"
20982141

2142+
- name: rhel8-test-atlas
2143+
display_name: Atlas Cluster Tests
2144+
run_on: rhel80-large
2145+
tasks:
2146+
- test_atlas_task_group
2147+
20992148
- name: plain-auth-test
21002149
display_name: "PLAIN (LDAP) Auth test"
21012150
run_on: rhel80-small
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
#!/bin/bash
2+
3+
set -o xtrace # Write all commands first to stderr
4+
set -o errexit # Exit the script with error if any of the commands fail
5+
6+
RELATIVE_DIR_PATH="$(dirname "${BASH_SOURCE[0]:-$0}")"
7+
. "${RELATIVE_DIR_PATH}/javaConfig.bash"
8+
9+
# compiled outside of lambda workflow. Note "SkipBuild: True" in template.yaml
10+
./gradlew -version
11+
./gradlew --info driver-lambda:shadowJar
12+
13+
. ${DRIVERS_TOOLS}/.evergreen/aws_lambda/run-deployed-lambda-aws-tests.sh

build.gradle

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -67,7 +67,7 @@ def coreProjects = subprojects - utilProjects
6767
def scalaProjects = subprojects.findAll { it.name.contains('scala') }
6868
def javaProjects = subprojects - scalaProjects
6969
def javaMainProjects = javaProjects - utilProjects
70-
def javaCodeCheckedProjects = javaMainProjects.findAll { !['driver-benchmarks', 'driver-workload-executor'].contains(it.name) }
70+
def javaCodeCheckedProjects = javaMainProjects.findAll { !['driver-benchmarks', 'driver-workload-executor', 'driver-lambda'].contains(it.name) }
7171
def javaAndScalaTestedProjects = javaCodeCheckedProjects + scalaProjects
7272

7373
configure(coreProjects) {

driver-lambda/build.gradle

Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
1+
/*
2+
* Copyright 2008-present MongoDB, Inc.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
buildscript {
18+
repositories {
19+
maven { url "https://plugins.gradle.org/m2/" }
20+
}
21+
dependencies {
22+
classpath 'com.github.jengelman.gradle.plugins:shadow:6.1.0'
23+
}
24+
}
25+
26+
plugins {
27+
id("application")
28+
}
29+
30+
apply plugin: 'application'
31+
apply plugin: 'com.github.johnrengelman.shadow'
32+
apply plugin: 'java'
33+
34+
compileJava {
35+
sourceCompatibility = JavaVersion.VERSION_11
36+
targetCompatibility = JavaVersion.VERSION_11
37+
}
38+
39+
mainClassName = "com.mongodb.workload.WorkloadExecutor"
40+
41+
sourceSets {
42+
main {
43+
java {
44+
srcDir 'src/main'
45+
}
46+
resources {
47+
srcDir 'src/resources'
48+
}
49+
}
50+
}
51+
52+
dependencies {
53+
implementation project(':driver-sync')
54+
implementation project(':bson')
55+
56+
implementation('com.amazonaws:aws-lambda-java-core:1.2.2')
57+
implementation('com.amazonaws:aws-lambda-java-events:3.11.1')
58+
}
59+
60+
61+
javadoc {
62+
enabled = false
63+
}
64+
65+
jar {
66+
manifest {
67+
attributes "Main-Class": "com.mongodb.lambdatest.LambdaTestApp"
68+
}
69+
}
70+
71+
shadowJar {
72+
archiveBaseName.set('lambdatest')
73+
archiveVersion.set('')
74+
}

driver-lambda/samconfig.toml

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
# More information about the configuration file can be found here:
2+
# https://docs.aws.amazon.com/serverless-application-model/latest/developerguide/serverless-sam-cli-config.html
3+
version = 0.1
4+
5+
[default]
6+
[default.global.parameters]
7+
stack_name = "lambdatest"
8+
9+
[default.build.parameters]
10+
cached = true
11+
parallel = false
12+
13+
[default.validate.parameters]
14+
lint = true
15+
16+
[default.deploy.parameters]
17+
capabilities = "CAPABILITY_IAM"
18+
confirm_changeset = false # headless
19+
resolve_s3 = true
20+
21+
[default.package.parameters]
22+
resolve_s3 = true
23+
24+
[default.sync.parameters]
25+
watch = true
26+
27+
[default.local_start_api.parameters]
28+
warm_containers = "EAGER"
29+
30+
[default.local_start_lambda.parameters]
31+
warm_containers = "EAGER"
Lines changed: 154 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,154 @@
1+
/*
2+
* Copyright 2008-present MongoDB, Inc.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package com.mongodb.lambdatest;
18+
19+
import com.amazonaws.services.lambda.runtime.Context;
20+
import com.amazonaws.services.lambda.runtime.RequestHandler;
21+
import com.amazonaws.services.lambda.runtime.events.APIGatewayProxyRequestEvent;
22+
import com.amazonaws.services.lambda.runtime.events.APIGatewayProxyResponseEvent;
23+
import com.mongodb.ConnectionString;
24+
import com.mongodb.MongoClientSettings;
25+
import com.mongodb.client.MongoClient;
26+
import com.mongodb.client.MongoClients;
27+
import com.mongodb.client.MongoCollection;
28+
import com.mongodb.event.CommandFailedEvent;
29+
import com.mongodb.event.CommandListener;
30+
import com.mongodb.event.CommandSucceededEvent;
31+
import com.mongodb.event.ConnectionClosedEvent;
32+
import com.mongodb.event.ConnectionCreatedEvent;
33+
import com.mongodb.event.ConnectionPoolListener;
34+
import com.mongodb.event.ServerHeartbeatFailedEvent;
35+
import com.mongodb.event.ServerHeartbeatSucceededEvent;
36+
import com.mongodb.event.ServerMonitorListener;
37+
import com.mongodb.lang.NonNull;
38+
import org.bson.BsonDocument;
39+
import org.bson.BsonInt64;
40+
import org.bson.BsonString;
41+
import org.bson.BsonValue;
42+
import org.bson.Document;
43+
44+
import java.io.PrintWriter;
45+
import java.io.StringWriter;
46+
import java.util.HashMap;
47+
import java.util.Map;
48+
49+
import static java.util.concurrent.TimeUnit.MILLISECONDS;
50+
51+
/**
52+
* Test App for AWS lambda functions
53+
*/
54+
public class LambdaTestApp implements RequestHandler<APIGatewayProxyRequestEvent, APIGatewayProxyResponseEvent> {
55+
private final MongoClient mongoClient;
56+
private long openConnections = 0;
57+
private long totalHeartbeatCount = 0;
58+
private long totalHeartbeatDurationMs = 0;
59+
private long totalCommandCount = 0;
60+
private long totalCommandDurationMs = 0;
61+
62+
public LambdaTestApp() {
63+
String connectionString = System.getenv("MONGODB_URI");
64+
65+
MongoClientSettings settings = MongoClientSettings.builder()
66+
.applyConnectionString(new ConnectionString(connectionString))
67+
.addCommandListener(new CommandListener() {
68+
@Override
69+
public void commandSucceeded(@NonNull final CommandSucceededEvent event) {
70+
totalCommandCount++;
71+
totalCommandDurationMs += event.getElapsedTime(MILLISECONDS);
72+
}
73+
@Override
74+
public void commandFailed(@NonNull final CommandFailedEvent event) {
75+
totalCommandCount++;
76+
totalCommandDurationMs += event.getElapsedTime(MILLISECONDS);
77+
}
78+
})
79+
.applyToServerSettings(builder -> builder.addServerMonitorListener(new ServerMonitorListener() {
80+
@Override
81+
public void serverHeartbeatSucceeded(@NonNull final ServerHeartbeatSucceededEvent event) {
82+
totalHeartbeatCount++;
83+
totalHeartbeatDurationMs += event.getElapsedTime(MILLISECONDS);
84+
}
85+
@Override
86+
public void serverHeartbeatFailed(@NonNull final ServerHeartbeatFailedEvent event) {
87+
totalHeartbeatCount++;
88+
totalHeartbeatDurationMs += event.getElapsedTime(MILLISECONDS);
89+
}
90+
}))
91+
.applyToConnectionPoolSettings(builder -> builder.addConnectionPoolListener(new ConnectionPoolListener() {
92+
@Override
93+
public void connectionCreated(@NonNull final ConnectionCreatedEvent event) {
94+
openConnections++;
95+
}
96+
@Override
97+
public void connectionClosed(@NonNull final ConnectionClosedEvent event) {
98+
openConnections--;
99+
}
100+
}))
101+
.build();
102+
mongoClient = MongoClients.create(settings);
103+
}
104+
105+
public APIGatewayProxyResponseEvent handleRequest(final APIGatewayProxyRequestEvent input, final Context context) {
106+
try {
107+
MongoCollection<Document> collection = mongoClient
108+
.getDatabase("lambdaTest")
109+
.getCollection("test");
110+
BsonValue id = collection.insertOne(new Document("n", 1)).getInsertedId();
111+
collection.deleteOne(new Document("_id", id));
112+
113+
BsonDocument responseBody = getBsonDocument();
114+
115+
return templateResponse()
116+
.withStatusCode(200)
117+
.withBody(responseBody.toJson());
118+
119+
} catch (Throwable e) {
120+
StringWriter sw = new StringWriter();
121+
e.printStackTrace(new PrintWriter(sw));
122+
BsonDocument responseBody = new BsonDocument()
123+
.append("throwable", new BsonString(e.getMessage()))
124+
.append("stacktrace", new BsonString(sw.toString()));
125+
return templateResponse()
126+
.withBody(responseBody.toJson())
127+
.withStatusCode(500);
128+
}
129+
}
130+
131+
private BsonDocument getBsonDocument() {
132+
BsonDocument responseBody = new BsonDocument()
133+
.append("totalCommandDurationMs", new BsonInt64(totalCommandDurationMs))
134+
.append("totalCommandCount", new BsonInt64(totalCommandCount))
135+
.append("totalHeartbeatDurationMs", new BsonInt64(totalHeartbeatDurationMs))
136+
.append("totalHeartbeatCount", new BsonInt64(totalHeartbeatCount))
137+
.append("openConnections", new BsonInt64(openConnections));
138+
139+
totalCommandDurationMs = 0;
140+
totalCommandCount = 0;
141+
totalHeartbeatCount = 0;
142+
totalHeartbeatDurationMs = 0;
143+
144+
return responseBody;
145+
}
146+
147+
private APIGatewayProxyResponseEvent templateResponse() {
148+
Map<String, String> headers = new HashMap<>();
149+
headers.put("Content-Type", "application/json");
150+
headers.put("X-Custom-Header", "application/json");
151+
return new APIGatewayProxyResponseEvent()
152+
.withHeaders(headers);
153+
}
154+
}

0 commit comments

Comments
 (0)