Skip to content

Commit 7c767d2

Browse files
committed
added lambda code
1 parent d2b8766 commit 7c767d2

File tree

16 files changed

+1868
-1
lines changed

16 files changed

+1868
-1
lines changed

workshop/lambda/README.md

Lines changed: 827 additions & 0 deletions
Large diffs are not rendered by default.
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
// Lambda Consumer App for Kinesis Stream
2+
3+
// Lambda Consumer
4+
const consumer = async( event ) => {
5+
try{
6+
for( const record of event.Records ) {
7+
const payload = record.kinesis;
8+
const message = Buffer.from(payload.data, 'base64').toString();
9+
10+
console.log(
11+
`Kinesis Message:
12+
partition key: ${payload.partitionKey}
13+
sequence number: ${payload.sequenceNumber}
14+
kinesis schema version: ${payload.kinesisSchemaVersion}
15+
data: ${message}`
16+
);
17+
}
18+
} catch ( error ) {
19+
console.log(error);
20+
}
21+
};
22+
23+
export { consumer };
Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
// Lambda Producer App for Kinesis Stream
2+
import { KinesisClient, PutRecordCommand } from "@aws-sdk/client-kinesis";
3+
4+
5+
// Lambda Producer
6+
const producer = async( event ) => {
7+
const kinesis = new KinesisClient({});
8+
9+
let statusCode = 200;
10+
let message;
11+
12+
if (!event.body) {
13+
return {
14+
statusCode: 400,
15+
body: JSON.stringify({
16+
message: "No message body found",
17+
}),
18+
};
19+
}
20+
21+
const streamName = process.env.KINESIS_STREAM;
22+
23+
try {
24+
await kinesis.send(
25+
new PutRecordCommand({
26+
StreamName: streamName,
27+
PartitionKey: "1234",
28+
Data: event.body,
29+
}),
30+
31+
message = `Message placed in the Event Stream: ${streamName}`
32+
)
33+
} catch ( error ) {
34+
console.log(error);
35+
message = error;
36+
statusCode = 500;
37+
}
38+
39+
return {
40+
statusCode,
41+
body: JSON.stringify({
42+
message,
43+
}),
44+
};
45+
};
46+
47+
export { producer };

workshop/lambda/auto/main.tf

Lines changed: 239 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,239 @@
1+
provider "aws" {
2+
region = "us-east-1"
3+
4+
default_tags {
5+
tags = {
6+
o11y-workshop = "lambda-tracing"
7+
}
8+
}
9+
}
10+
11+
12+
# Get IAM Role
13+
data "aws_caller_identity" "current" {}
14+
resource "aws_iam_role" "lambda_kinesis" {
15+
name = "lambda_kinesis"
16+
assume_role_policy = jsonencode({
17+
Version = "2012-10-17"
18+
Statement = [
19+
{
20+
Action = "sts:AssumeRole"
21+
Effect = "Allow"
22+
Principal = {
23+
Service = "lambda.amazonaws.com"
24+
}
25+
}
26+
]
27+
})
28+
}
29+
resource "aws_iam_role_policy_attachment" "lambda_kinesis_execution" {
30+
role = aws_iam_role.lambda_kinesis.name
31+
policy_arn = "arn:aws:iam::aws:policy/AmazonKinesisFullAccess"
32+
}
33+
34+
# Create S3 Bucket, Ownership, ACL
35+
resource "aws_s3_bucket" "lambda_bucket" {
36+
bucket = "${var.prefix}-lambda-code"
37+
}
38+
39+
resource "aws_s3_bucket_ownership_controls" "lambda_bucket" {
40+
bucket = aws_s3_bucket.lambda_bucket.id
41+
rule {
42+
object_ownership = "BucketOwnerPreferred"
43+
}
44+
}
45+
46+
resource "aws_s3_bucket_acl" "lambda_bucket" {
47+
depends_on = [aws_s3_bucket_ownership_controls.lambda_bucket]
48+
49+
bucket = aws_s3_bucket.lambda_bucket.id
50+
acl = "private"
51+
}
52+
53+
54+
# Package Producer and Consumer Apps, Upload to S3 Bucket
55+
data "archive_file" "producer_app" {
56+
type = "zip"
57+
58+
source_file = "${path.module}/handler/producer.mjs"
59+
output_path = "${path.module}/lambda_producer.zip"
60+
}
61+
62+
resource "aws_s3_object" "producer_app" {
63+
bucket = aws_s3_bucket.lambda_bucket.id
64+
65+
key = "lambda_producer.zip"
66+
source = data.archive_file.producer_app.output_path
67+
68+
etag = filemd5(data.archive_file.producer_app.output_path)
69+
}
70+
71+
data "archive_file" "consumer_app" {
72+
type = "zip"
73+
74+
source_file = "${path.module}/handler/consumer.mjs"
75+
output_path = "${path.module}/lambda_consumer.zip"
76+
}
77+
78+
resource "aws_s3_object" "consumer_app" {
79+
bucket = aws_s3_bucket.lambda_bucket.id
80+
81+
key = "lambda_consumer.zip"
82+
source = data.archive_file.consumer_app.output_path
83+
84+
etag = filemd5(data.archive_file.consumer_app.output_path)
85+
}
86+
87+
88+
# Create Lambda Functions
89+
resource "aws_lambda_function" "lambda_producer" {
90+
function_name = "${var.prefix}-producer"
91+
92+
s3_bucket = aws_s3_bucket.lambda_bucket.id
93+
s3_key = aws_s3_object.producer_app.key
94+
95+
runtime = "nodejs20.x"
96+
handler = "producer.producer"
97+
98+
source_code_hash = data.archive_file.producer_app.output_base64sha256
99+
100+
role = aws_iam_role.lambda_kinesis.arn
101+
102+
environment {
103+
variables = {
104+
SPLUNK_ACCESS_TOKEN = var.o11y_access_token
105+
SPLUNK_REALM = var.o11y_realm
106+
OTEL_SERVICE_NAME = "${var.prefix}-producer-lambda"
107+
OTEL_RESOURCE_ATTRIBUTES = "deployment.environment=${var.prefix}-lambda-shop"
108+
AWS_LAMBDA_EXEC_WRAPPER = "/opt/nodejs-otel-handler"
109+
KINESIS_STREAM = aws_kinesis_stream.lambda_stream.name
110+
}
111+
}
112+
113+
layers = var.otel_lambda_layer
114+
115+
timeout = 60
116+
}
117+
118+
resource "aws_lambda_function" "lambda_consumer" {
119+
function_name = "${var.prefix}-consumer"
120+
121+
s3_bucket = aws_s3_bucket.lambda_bucket.id
122+
s3_key = aws_s3_object.consumer_app.key
123+
124+
runtime = "nodejs20.x"
125+
handler = "consumer.consumer"
126+
127+
source_code_hash = data.archive_file.consumer_app.output_base64sha256
128+
129+
role = aws_iam_role.lambda_kinesis.arn
130+
131+
environment {
132+
variables = {
133+
SPLUNK_ACCESS_TOKEN = var.o11y_access_token
134+
SPLUNK_REALM = var.o11y_realm
135+
OTEL_SERVICE_NAME = "${var.prefix}-consumer-lambda"
136+
OTEL_RESOURCE_ATTRIBUTES = "deployment.environment=${var.prefix}-lambda-shop"
137+
AWS_LAMBDA_EXEC_WRAPPER = "/opt/nodejs-otel-handler"
138+
}
139+
}
140+
141+
layers = var.otel_lambda_layer
142+
143+
timeout = 60
144+
}
145+
146+
# Add API Gateway API, Stage, Integration, Route and Permission Resources
147+
resource "aws_apigatewayv2_api" "lambda" {
148+
name = "${var.prefix}-gateway"
149+
protocol_type = "HTTP"
150+
}
151+
152+
resource "aws_apigatewayv2_stage" "lambda" {
153+
api_id = aws_apigatewayv2_api.lambda.id
154+
155+
name = "${var.prefix}-gw-stage"
156+
auto_deploy = true
157+
158+
access_log_settings {
159+
destination_arn = aws_cloudwatch_log_group.api_gw.arn
160+
161+
format = jsonencode({
162+
requestId = "$context.requestId"
163+
sourceIp = "$context.identity.sourceIp"
164+
requestTime = "$context.requestTime"
165+
protocol = "$context.protocol"
166+
httpMethod = "$context.httpMethod"
167+
resourcePath = "$context.resourcePath"
168+
routeKey = "$context.routeKey"
169+
status = "$context.status"
170+
responseLength = "$context.responseLength"
171+
integrationErrorMessage = "$context.integrationErrorMessage"
172+
}
173+
)
174+
}
175+
}
176+
177+
resource "aws_apigatewayv2_integration" "lambda" {
178+
api_id = aws_apigatewayv2_api.lambda.id
179+
180+
integration_uri = aws_lambda_function.lambda_producer.invoke_arn
181+
integration_type = "AWS_PROXY"
182+
integration_method = "POST"
183+
}
184+
185+
resource "aws_apigatewayv2_route" "lambda" {
186+
api_id = aws_apigatewayv2_api.lambda.id
187+
188+
route_key = "POST /producer"
189+
target = "integrations/${aws_apigatewayv2_integration.lambda.id}"
190+
}
191+
192+
# Lambda Permission for API Gateway
193+
resource "aws_lambda_permission" "api_gw" {
194+
statement_id = "AllowExecutionFromAPIGateway"
195+
action = "lambda:InvokeFunction"
196+
function_name = aws_lambda_function.lambda_producer.function_name
197+
principal = "apigateway.amazonaws.com"
198+
199+
source_arn = "${aws_apigatewayv2_api.lambda.execution_arn}/*/*"
200+
}
201+
202+
# CloudWatch Log Groups for Lambda and API Gateway
203+
resource "aws_cloudwatch_log_group" "lambda_producer" {
204+
name = "/aws/lambda/${aws_lambda_function.lambda_producer.function_name}"
205+
206+
retention_in_days = 30
207+
}
208+
209+
resource "aws_cloudwatch_log_group" "lambda_consumer" {
210+
name = "/aws/lambda/${aws_lambda_function.lambda_consumer.function_name}"
211+
212+
retention_in_days = 30
213+
}
214+
215+
resource "aws_cloudwatch_log_group" "api_gw" {
216+
name = "/aws/api_gw/${aws_apigatewayv2_api.lambda.name}"
217+
218+
retention_in_days = 30
219+
}
220+
221+
# Kinesis Data Stream
222+
resource "aws_kinesis_stream" "lambda_stream" {
223+
name = "${var.prefix}-lambda_stream"
224+
shard_count = 1
225+
retention_period = 24
226+
shard_level_metrics = [
227+
"IncomingBytes",
228+
"OutgoingBytes"
229+
]
230+
}
231+
232+
# Source Mapping Lambda Consumer to Kinesis Stream
233+
resource "aws_lambda_event_source_mapping" "kinesis_lambda_event_mapping" {
234+
batch_size = 100
235+
event_source_arn = aws_kinesis_stream.lambda_stream.arn
236+
enabled = true
237+
function_name = aws_lambda_function.lambda_consumer.arn
238+
starting_position = "LATEST"
239+
}

workshop/lambda/auto/outputs.tf

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
# Output value definitions
2+
3+
4+
output "lambda_bucket_name" {
5+
description = "S3 Bucket"
6+
value = aws_s3_bucket.lambda_bucket.id
7+
}
8+
9+
output "base_url" {
10+
description = "Endpoint"
11+
value = "${aws_apigatewayv2_stage.lambda.invoke_url}/producer"
12+
}
13+
14+
output "producer_function_name" {
15+
description = "Lambda Producer"
16+
value = aws_lambda_function.lambda_producer.function_name
17+
}
18+
19+
output "producer_log_group_name" {
20+
description = "Log Group Name for the Lambda Producer"
21+
value = aws_cloudwatch_log_group.lambda_producer.name
22+
}
23+
24+
output "producer_log_group_arn" {
25+
description = "Log Group ARN for the Lambda Producer"
26+
value = aws_cloudwatch_log_group.lambda_producer.arn
27+
}
28+
29+
output "consumer_function_name" {
30+
description = "Lambda Consumer"
31+
value = aws_lambda_function.lambda_consumer.function_name
32+
}
33+
34+
output "consumer_log_group_name" {
35+
description = "Log Group Name for the Lambda Consumer"
36+
value = aws_cloudwatch_log_group.lambda_consumer.name
37+
}
38+
39+
output "consumer_log_group_arn" {
40+
description = "Log Group ARN for the Lambda Consumer"
41+
value = aws_cloudwatch_log_group.lambda_consumer.arn
42+
}
43+
44+
output "environment" {
45+
description = "Deployment Environment"
46+
value = "${var.prefix}-lambda-shop"
47+
}

0 commit comments

Comments
 (0)