Skip to content
This repository was archived by the owner on Mar 30, 2021. It is now read-only.

Commit f1e2005

Browse files
authored
Merge pull request #1 from kazfuku/master
add documentdb and lambda triggers samples
2 parents d40c9c2 + 580487c commit f1e2005

20 files changed

+908
-12
lines changed

README.md

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,8 @@ It contains:
1818
2. `mvn install` package lambda source files into jar and install to maven local repository
1919
3. `cd ../cdk`
2020
4. `mvn compile` compile cdk source files
21-
5. `cdk deploy` deploy resources via cdk
21+
5. `cdk bootstrap` deploy bootstrap stack (If you deploy first at region)
22+
6. `cdk deploy` deploy resources via cdk
2223

2324
## License
2425

cdk/pom.xml

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,11 @@
7070
<artifactId>lambda</artifactId>
7171
<version>${aws.cdk.version}</version>
7272
</dependency>
73+
<dependency>
74+
<groupId>software.amazon.awscdk</groupId>
75+
<artifactId>lambda-event-sources</artifactId>
76+
<version>${aws.cdk.version}</version>
77+
</dependency>
7378
<dependency>
7479
<groupId>software.amazon.awscdk</groupId>
7580
<artifactId>s3</artifactId>
@@ -95,6 +100,26 @@
95100
<artifactId>secretsmanager</artifactId>
96101
<version>${aws.cdk.version}</version>
97102
</dependency>
103+
<dependency>
104+
<groupId>software.amazon.awscdk</groupId>
105+
<artifactId>docdb</artifactId>
106+
<version>${aws.cdk.version}</version>
107+
</dependency>
108+
<dependency>
109+
<groupId>software.amazon.awscdk</groupId>
110+
<artifactId>kinesis</artifactId>
111+
<version>${aws.cdk.version}</version>
112+
</dependency>
113+
<dependency>
114+
<groupId>software.amazon.awscdk</groupId>
115+
<artifactId>sqs</artifactId>
116+
<version>${aws.cdk.version}</version>
117+
</dependency>
118+
<dependency>
119+
<groupId>software.amazon.awscdk</groupId>
120+
<artifactId>sns</artifactId>
121+
<version>${aws.cdk.version}</version>
122+
</dependency>
98123

99124
<!-- https://mvnrepository.com/artifact/junit/junit -->
100125
<dependency>

cdk/src/main/java/com/amazon/aws/prototyping/CdkStack.java

Lines changed: 156 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,28 @@
11
package com.amazon.aws.prototyping;
22

3+
import java.util.ArrayList;
34
import java.util.Arrays;
5+
import java.util.Collections;
6+
import java.util.HashMap;
47
import java.util.List;
8+
import java.util.Map;
9+
10+
import org.apache.commons.collections4.CollectionUtils;
11+
12+
import com.amazon.aws.prototyping.apigateway.DocumentDbFunction;
13+
import com.amazon.aws.prototyping.apigateway.DynamoDBFunction;
14+
import com.amazon.aws.prototyping.apigateway.EC2Function;
15+
import com.amazon.aws.prototyping.apigateway.HttpFunction;
16+
import com.amazon.aws.prototyping.apigateway.JdbcFunction;
17+
import com.amazon.aws.prototyping.apigateway.JsonFunction;
18+
import com.amazon.aws.prototyping.apigateway.KinesisProduceFunction;
19+
import com.amazon.aws.prototyping.apigateway.S3Function;
20+
import com.amazon.aws.prototyping.apigateway.SnsFunction;
21+
import com.amazon.aws.prototyping.apigateway.SqsFunction;
22+
import com.amazon.aws.prototyping.eventsource.DynamoDBStreamFunction;
23+
import com.amazon.aws.prototyping.eventsource.KinesisConsumeFunction;
24+
import com.amazon.aws.prototyping.eventsource.SnsSubscribedFunction;
25+
import com.amazon.aws.prototyping.eventsource.SqsReceiveFunction;
526

627
import software.amazon.awscdk.core.Construct;
728
import software.amazon.awscdk.core.Duration;
@@ -12,9 +33,14 @@
1233
import software.amazon.awscdk.services.apigateway.Resource;
1334
import software.amazon.awscdk.services.apigateway.RestApi;
1435
import software.amazon.awscdk.services.apigateway.StageOptions;
36+
import software.amazon.awscdk.services.docdb.CfnDBCluster;
37+
import software.amazon.awscdk.services.docdb.CfnDBClusterParameterGroup;
38+
import software.amazon.awscdk.services.docdb.CfnDBInstance;
39+
import software.amazon.awscdk.services.docdb.CfnDBSubnetGroup;
1540
import software.amazon.awscdk.services.dynamodb.Attribute;
1641
import software.amazon.awscdk.services.dynamodb.AttributeType;
1742
import software.amazon.awscdk.services.dynamodb.GlobalSecondaryIndexProps;
43+
import software.amazon.awscdk.services.dynamodb.StreamViewType;
1844
import software.amazon.awscdk.services.dynamodb.Table;
1945
import software.amazon.awscdk.services.ec2.AmazonLinuxGeneration;
2046
import software.amazon.awscdk.services.ec2.AmazonLinuxImage;
@@ -31,14 +57,22 @@
3157
import software.amazon.awscdk.services.ec2.Vpc;
3258
import software.amazon.awscdk.services.iam.Effect;
3359
import software.amazon.awscdk.services.iam.PolicyStatement;
60+
import software.amazon.awscdk.services.kinesis.Stream;
3461
import software.amazon.awscdk.services.lambda.Code;
3562
import software.amazon.awscdk.services.lambda.Function;
3663
import software.amazon.awscdk.services.lambda.Runtime;
64+
import software.amazon.awscdk.services.lambda.StartingPosition;
65+
import software.amazon.awscdk.services.lambda.eventsources.DynamoEventSource;
66+
import software.amazon.awscdk.services.lambda.eventsources.KinesisEventSource;
67+
import software.amazon.awscdk.services.lambda.eventsources.SnsEventSource;
68+
import software.amazon.awscdk.services.lambda.eventsources.SqsEventSource;
3769
import software.amazon.awscdk.services.rds.DatabaseInstance;
3870
import software.amazon.awscdk.services.rds.DatabaseInstanceEngine;
3971
import software.amazon.awscdk.services.s3.Bucket;
4072
import software.amazon.awscdk.services.secretsmanager.Secret;
4173
import software.amazon.awscdk.services.secretsmanager.SecretStringGenerator;
74+
import software.amazon.awscdk.services.sns.Topic;
75+
import software.amazon.awscdk.services.sqs.Queue;
4276

4377
public class CdkStack extends Stack {
4478
public CdkStack(final Construct scope, final String id) {
@@ -48,8 +82,8 @@ public CdkStack(final Construct scope, final String id) {
4882
public CdkStack(final Construct scope, final String id, final StackProps props) {
4983
super(scope, id, props);
5084

51-
StageOptions deployOptions = StageOptions.builder().loggingLevel(MethodLoggingLevel.INFO).dataTraceEnabled(true)
52-
.build();
85+
StageOptions deployOptions =
86+
StageOptions.builder().loggingLevel(MethodLoggingLevel.INFO).dataTraceEnabled(true).build();
5387
RestApi api = RestApi.Builder.create(this, "JavaSamplesRestApi").deployOptions(deployOptions).build();
5488

5589
List<SubnetConfiguration> subnetConfigurations = Arrays.asList(
@@ -63,6 +97,10 @@ public CdkStack(final Construct scope, final String id, final StackProps props)
6397
createEC2Sample(api, vpc);
6498
createJsonSample(api);
6599
createRdbSample(api, vpc);
100+
createDocumentDbSample(api, vpc);
101+
createKinesisSample(api);
102+
createSqlSample(api);
103+
createSnsSample(api);
66104
}
67105

68106
private void createHttpSample(RestApi api) {
@@ -95,7 +133,7 @@ private void createDynamoDbSample(RestApi api) {
95133
Attribute attributeYear = Attribute.builder().name("year").type(AttributeType.NUMBER).build();
96134
Attribute attributeTitle = Attribute.builder().name("title").type(AttributeType.STRING).build();
97135
Table table = Table.Builder.create(this, "SampleTable").partitionKey(attributeYear).sortKey(attributeTitle)
98-
.build();
136+
.stream(StreamViewType.NEW_AND_OLD_IMAGES).build();
99137
GlobalSecondaryIndexProps index = GlobalSecondaryIndexProps.builder().indexName("title-year-index")
100138
.partitionKey(attributeTitle).sortKey(attributeYear).build();
101139
table.addGlobalSecondaryIndex(index);
@@ -112,6 +150,11 @@ private void createDynamoDbSample(RestApi api) {
112150
Resource dynamodbResource = api.getRoot().addResource("dynamodb");
113151
dynamodbResource.addMethod("GET", LambdaIntegration.Builder.create(getFunction).proxy(true).build());
114152
dynamodbResource.addMethod("PUT", LambdaIntegration.Builder.create(putFunction).proxy(true).build());
153+
154+
Function streamFunction = createFunction(DynamoDBStreamFunction.class, "handle");
155+
DynamoEventSource eventSource =
156+
DynamoEventSource.Builder.create(table).startingPosition(StartingPosition.LATEST).build();
157+
streamFunction.addEventSource(eventSource);
115158
}
116159

117160
private void createEC2Sample(RestApi api, Vpc vpc) {
@@ -121,20 +164,20 @@ private void createEC2Sample(RestApi api, Vpc vpc) {
121164
.instanceType(InstanceType.of(InstanceClass.BURSTABLE2, InstanceSize.MICRO)).vpc(vpc).build();
122165

123166
PolicyStatement describeInstancesPolicy = PolicyStatement.Builder.create().effect(Effect.ALLOW)
124-
.actions(Arrays.asList("ec2:describeInstances")).resources(Arrays.asList("*")).build();
167+
.actions(Collections.singletonList("ec2:describeInstances")).resources(Arrays.asList("*")).build();
125168

126169
Function startFunction = createFunction(EC2Function.class, "startAndWait", Duration.minutes(3), null);
127170
startFunction.addEnvironment("INSTANCE_ID", instance.getInstanceId());
128171
startFunction.addToRolePolicy(PolicyStatement.Builder.create().effect(Effect.ALLOW)
129-
.actions(Arrays.asList("ec2:startInstances")).resources(Arrays.asList(String
172+
.actions(Collections.singletonList("ec2:startInstances")).resources(Arrays.asList(String
130173
.format("arn:aws:ec2:%s:%s:instance/%s", getRegion(), getAccount(), instance.getInstanceId())))
131174
.build());
132175
startFunction.addToRolePolicy(describeInstancesPolicy);
133176

134177
Function stopFunction = createFunction(EC2Function.class, "stopAndWait", Duration.minutes(3), null);
135178
stopFunction.addEnvironment("INSTANCE_ID", instance.getInstanceId());
136179
stopFunction.addToRolePolicy(PolicyStatement.Builder.create().effect(Effect.ALLOW)
137-
.actions(Arrays.asList("ec2:stopInstances")).resources(Arrays.asList(String
180+
.actions(Collections.singletonList("ec2:stopInstances")).resources(Arrays.asList(String
138181
.format("arn:aws:ec2:%s:%s:instance/%s", getRegion(), getAccount(), instance.getInstanceId())))
139182
.build());
140183
stopFunction.addToRolePolicy(describeInstancesPolicy);
@@ -158,11 +201,12 @@ private void createJsonSample(RestApi api) {
158201
}
159202

160203
private void createRdbSample(RestApi api, Vpc vpc) {
161-
Secret secret = Secret.Builder.create(this, "RdsSampleUserPassword")
162-
.generateSecretString(
163-
SecretStringGenerator.builder().secretStringTemplate(String.format("{\"username\": \"test\"}"))
204+
Secret secret =
205+
Secret.Builder.create(this, "RdsSampleUserPassword")
206+
.generateSecretString(SecretStringGenerator.builder()
207+
.secretStringTemplate(String.format("{\"username\": \"test\"}"))
164208
.generateStringKey("password").excludePunctuation(true).build())
165-
.build();
209+
.build();
166210

167211
String databaseName = "sample";
168212
SecurityGroup rdsSecurityGroup = SecurityGroup.Builder.create(this, "SampleRdsSecurityGroup").vpc(vpc).build();
@@ -193,6 +237,108 @@ private void createRdbSample(RestApi api, Vpc vpc) {
193237
LambdaIntegration.Builder.create(selectFunction).proxy(true).build());
194238
}
195239

240+
private void createDocumentDbSample(RestApi api, Vpc vpc) {
241+
Secret secret = Secret.Builder.create(this, "DocumentDbSampleUserPassword")
242+
.generateSecretString(SecretStringGenerator.builder().secretStringTemplate("{\"username\": \"test\"}")
243+
.generateStringKey("password").excludePunctuation(true).build())
244+
.build();
245+
246+
SecurityGroup securityGroup =
247+
SecurityGroup.Builder.create(this, "SampleDocumentDBSecurityGroup").vpc(vpc).build();
248+
securityGroup.addIngressRule(Peer.ipv4(vpc.getVpcCidrBlock()), Port.tcp(27017));
249+
250+
Map<String, String> parameterMap = new HashMap<>();
251+
parameterMap.put("tls", "disabled");
252+
CfnDBClusterParameterGroup parameterGroup = CfnDBClusterParameterGroup.Builder
253+
.create(this, "SampleDocumentDBParameterGroup").name("sample-document-db-parameter-group")
254+
.description("DBParameterGroup for SampleDocumentDBCluster").family("docdb3.6").parameters(parameterMap)
255+
.build();
256+
257+
List<String> subnetIds =
258+
new ArrayList<>(CollectionUtils.collect(vpc.getPrivateSubnets(), (subnet) -> subnet.getSubnetId()));
259+
// both db subnet group name and cluster name need to be lower case, because it
260+
// is changed to lower case by cdk for some reason.
261+
CfnDBSubnetGroup subnetGroup = CfnDBSubnetGroup.Builder.create(this, "SampleDocumentDBSubnetGroup")
262+
.subnetIds(subnetIds).dbSubnetGroupName("sample-document-db-subnet-group")
263+
.dbSubnetGroupDescription("DBSubnetGroup for SampleDocumentDBCluster").build();
264+
265+
CfnDBCluster cluster = CfnDBCluster.Builder.create(this, "SampleDocumentDBCluster")
266+
.dbClusterIdentifier("sample-document-db-cluster").dbClusterParameterGroupName(parameterGroup.getName())
267+
.dbSubnetGroupName(subnetGroup.getDbSubnetGroupName())
268+
.vpcSecurityGroupIds(Collections.singletonList(securityGroup.getSecurityGroupId()))
269+
.masterUsername(secret.secretValueFromJson("username").toString())
270+
.masterUserPassword(secret.secretValueFromJson("password").toString()).build();
271+
cluster.addDependsOn(subnetGroup);
272+
273+
CfnDBInstance instance = CfnDBInstance.Builder.create(this, "SampleDocumentDB")
274+
.dbClusterIdentifier(cluster.getDbClusterIdentifier()).dbInstanceClass("db.r5.large").build();
275+
instance.addDependsOn(cluster);
276+
277+
Function insertFunction = createFunction(DocumentDbFunction.class, "insert", Duration.seconds(30), vpc);
278+
Function deleteFunction = createFunction(DocumentDbFunction.class, "delete", Duration.seconds(30), vpc);
279+
Function findFunction = createFunction(DocumentDbFunction.class, "find", Duration.seconds(30), vpc);
280+
281+
for (Function function : Arrays.asList(insertFunction, deleteFunction, findFunction)) {
282+
function.addEnvironment("CLUSTER_ENDPOINT", cluster.getAttrEndpoint());
283+
function.addEnvironment("SECRET_ARN", secret.getSecretArn());
284+
secret.grantRead(function);
285+
}
286+
287+
Resource documentDbResource = api.getRoot().addResource("documentdb");
288+
documentDbResource.addResource("insert").addMethod("POST",
289+
LambdaIntegration.Builder.create(insertFunction).proxy(true).build());
290+
documentDbResource.addResource("delete").addMethod("POST",
291+
LambdaIntegration.Builder.create(deleteFunction).proxy(true).build());
292+
documentDbResource.addResource("find").addMethod("GET",
293+
LambdaIntegration.Builder.create(findFunction).proxy(true).build());
294+
}
295+
296+
private void createKinesisSample(RestApi api) {
297+
Stream stream = Stream.Builder.create(this, "SampleKinesisStream").build();
298+
299+
Function putRecordFunction = createFunction(KinesisProduceFunction.class, "putRecord");
300+
putRecordFunction.addEnvironment("KINESIS_STREAM_NAME", stream.getStreamName());
301+
stream.grantWrite(putRecordFunction);
302+
303+
api.getRoot().addResource("kinesis").addMethod("PUT",
304+
LambdaIntegration.Builder.create(putRecordFunction).proxy(true).build());
305+
306+
Function function = createFunction(KinesisConsumeFunction.class, "handle");
307+
308+
KinesisEventSource eventSource =
309+
KinesisEventSource.Builder.create(stream).startingPosition(StartingPosition.TRIM_HORIZON).build();
310+
function.addEventSource(eventSource);
311+
}
312+
313+
private void createSqlSample(RestApi api) {
314+
Queue queue = Queue.Builder.create(this, "SampleSqsQueue").build();
315+
316+
Function sendFunction = createFunction(SqsFunction.class, "send");
317+
queue.grantSendMessages(sendFunction);
318+
sendFunction.addEnvironment("QUEUE_URL", queue.getQueueUrl());
319+
320+
api.getRoot().addResource("sqs").addMethod("PUT",
321+
LambdaIntegration.Builder.create(sendFunction).proxy(true).build());
322+
323+
Function receiveFunction = createFunction(SqsReceiveFunction.class, "handle");
324+
SqsEventSource eventSource = SqsEventSource.Builder.create(queue).build();
325+
receiveFunction.addEventSource(eventSource);
326+
}
327+
328+
private void createSnsSample(RestApi api) {
329+
Topic topic = Topic.Builder.create(this, "SampleTopic").build();
330+
331+
Function publishFunction = createFunction(SnsFunction.class, "publish");
332+
topic.grantPublish(publishFunction);
333+
publishFunction.addEnvironment("TOPIC_ARN", topic.getTopicArn());
334+
335+
api.getRoot().addResource("sns").addMethod("POST",
336+
LambdaIntegration.Builder.create(publishFunction).proxy(true).build());
337+
338+
Function subscribedFunction = createFunction(SnsSubscribedFunction.class, "handle");
339+
subscribedFunction.addEventSource(new SnsEventSource(topic));
340+
}
341+
196342
private Function createFunction(Class<?> functionClass, String handler) {
197343
return createFunction(functionClass, handler, Duration.seconds(10), null);
198344
}

lambda/.gitignore

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
target
2+
.settings
3+
.classpath
4+
.project

lambda/pom.xml

Lines changed: 28 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@
3535
</build>
3636
<properties>
3737
<aws.sdk.version>1.11.695</aws.sdk.version>
38+
<jackson.version>2.10.1</jackson.version>
3839
</properties>
3940
<dependencies>
4041
<dependency>
@@ -72,21 +73,47 @@
7273
<artifactId>aws-java-sdk-dynamodb</artifactId>
7374
<version>${aws.sdk.version}</version>
7475
</dependency>
76+
<dependency>
77+
<groupId>com.amazonaws</groupId>
78+
<artifactId>aws-java-sdk-kinesis</artifactId>
79+
<version>${aws.sdk.version}</version>
80+
</dependency>
81+
<dependency>
82+
<groupId>com.amazonaws</groupId>
83+
<artifactId>aws-java-sdk-sqs</artifactId>
84+
<version>${aws.sdk.version}</version>
85+
</dependency>
86+
<dependency>
87+
<groupId>com.amazonaws</groupId>
88+
<artifactId>aws-java-sdk-sns</artifactId>
89+
<version>${aws.sdk.version}</version>
90+
</dependency>
7591
<dependency>
7692
<groupId>mysql</groupId>
7793
<artifactId>mysql-connector-java</artifactId>
7894
<version>8.0.18</version>
7995
</dependency>
96+
<!-- use same version in jackson core and dataformat-cbor for using kinesis sdk -->
8097
<dependency>
8198
<groupId>com.fasterxml.jackson.core</groupId>
8299
<artifactId>jackson-databind</artifactId>
83-
<version>2.10.1</version>
100+
<version>${jackson.version}</version>
101+
</dependency>
102+
<dependency>
103+
<groupId>com.fasterxml.jackson.dataformat</groupId>
104+
<artifactId>jackson-dataformat-cbor</artifactId>
105+
<version>${jackson.version}</version>
84106
</dependency>
85107
<dependency>
86108
<groupId>org.apache.httpcomponents</groupId>
87109
<artifactId>httpclient</artifactId>
88110
<version>4.5.10</version>
89111
</dependency>
112+
<dependency>
113+
<groupId>org.mongodb</groupId>
114+
<artifactId>mongodb-driver</artifactId>
115+
<version>3.12.3</version>
116+
</dependency>
90117
<dependency>
91118
<groupId>commons-io</groupId>
92119
<artifactId>commons-io</artifactId>
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
package com.amazon.aws.prototyping.apigateway;
2+
3+
import com.amazonaws.services.lambda.runtime.events.APIGatewayProxyResponseEvent;
4+
5+
abstract class AbstractFunction {
6+
protected APIGatewayProxyResponseEvent ok() {
7+
APIGatewayProxyResponseEvent response = new APIGatewayProxyResponseEvent();
8+
response.setStatusCode(Integer.valueOf(200));
9+
response.setBody("ok");
10+
return response;
11+
}
12+
}

0 commit comments

Comments
 (0)