-
Notifications
You must be signed in to change notification settings - Fork 109
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
fix: add lambda to return rate limit
- Loading branch information
Gerald Iakobinyi-Pich
committed
Dec 16, 2024
1 parent
d428100
commit 533006c
Showing
5 changed files
with
217 additions
and
11 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -15,3 +15,4 @@ postgres_db_passport_data | |
.DS_Store | ||
**/.next/ | ||
infra/aws/python | ||
__lambda__ |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,173 @@ | ||
import * as pulumi from "@pulumi/pulumi"; | ||
import * as archive from "@pulumi/archive"; | ||
import * as aws from "@pulumi/aws"; | ||
import { ListenerRule } from "@pulumi/aws/lb"; | ||
import { Listener } from "@pulumi/aws/alb"; | ||
import { secretsManager } from "infra-libs"; | ||
import { defaultTags, stack } from "../../lib/tags"; | ||
|
||
import { createLambdaFunction } from "../../lib/lambda"; | ||
|
||
export function createEmbedLambdaRateLimit(config: { | ||
name: string; | ||
snsAlertsTopicArn: pulumi.Input<string>; | ||
httpsListener: pulumi.Output<Listener>; | ||
ceramicCacheScorerId: number; | ||
scorerSecret: aws.secretsmanager.Secret; | ||
privateSubnetSecurityGroup: aws.ec2.SecurityGroup; | ||
vpcId: pulumi.Input<string>; | ||
vpcPrivateSubnetIds: pulumi.Input<any>; | ||
lambdaLayerArn: pulumi.Input<string>; | ||
bucketId: pulumi.Input<string>; | ||
}) { | ||
const apiLambdaEnvironment = [ | ||
...secretsManager.getEnvironmentVars({ | ||
vault: "DevOps", | ||
repo: "passport-scorer", | ||
env: stack, | ||
section: "api", | ||
}), | ||
{ | ||
name: "DEBUG", | ||
value: "off", | ||
}, | ||
{ | ||
name: "LOGGING_STRATEGY", | ||
value: "structlog_json", | ||
}, | ||
{ | ||
name: "FF_API_ANALYTICS", | ||
value: "on", | ||
}, | ||
{ | ||
name: "CERAMIC_CACHE_SCORER_ID", | ||
value: `${config.ceramicCacheScorerId}`, | ||
}, | ||
{ | ||
name: "SCORER_SERVER_SSM_ARN", | ||
value: config.scorerSecret.arn, | ||
}, | ||
{ | ||
name: "VERIFIER_URL", | ||
value: "http://core-alb.private.gitcoin.co/verifier/verify", | ||
}, | ||
].sort(secretsManager.sortByName); | ||
|
||
// The lambda will contain our own code (everything from the `api` folder for now) | ||
const lambdaCode = archive.getFile({ | ||
type: "zip", | ||
sourceDir: "../../api", | ||
outputPath: "lambda_function_payload.zip", | ||
excludes: ["**/__pycache__"], | ||
}); | ||
|
||
const lambdaName = `${config.name}-lambda`; | ||
const { lambdaFunction, lambdaFunctionUrl } = createLambdaFunction( | ||
[config.scorerSecret.arn], | ||
config.vpcId, | ||
config.vpcPrivateSubnetIds, | ||
{ | ||
name: lambdaName, | ||
description: "Retreive the rate limit for an API key", | ||
code: new pulumi.asset.FileArchive("lambda_function_payload.zip"), | ||
// role: lambdaRole.arn, | ||
handler: "embed.lambda.lambda_handler_get_rate_limit", // TODO: change this | ||
sourceCodeHash: lambdaCode.then((archive) => archive.outputBase64sha256), | ||
runtime: aws.lambda.Runtime.Python3d12, | ||
environment: { | ||
variables: apiLambdaEnvironment.reduce( | ||
( | ||
acc: { [key: string]: pulumi.Input<string> }, | ||
e: { name: string; value: pulumi.Input<string> } | ||
) => { | ||
acc[e.name] = e.value; | ||
return acc; | ||
}, | ||
{} | ||
), | ||
}, | ||
memorySize: 128, | ||
timeout: 60, | ||
layers: [config.lambdaLayerArn], | ||
tags: { | ||
...defaultTags, | ||
Name: lambdaName, | ||
}, | ||
} | ||
); | ||
|
||
// Create alarm to monitor lambda errors | ||
const metricAlarmName = `${config.name}-lambda-errors`; | ||
const lambdaErrorsAlarm = new aws.cloudwatch.MetricAlarm(metricAlarmName, { | ||
tags: { ...defaultTags, Name: metricAlarmName }, | ||
alarmActions: [config.snsAlertsTopicArn], | ||
okActions: [config.snsAlertsTopicArn], | ||
comparisonOperator: "GreaterThanOrEqualToThreshold", | ||
dimensions: { | ||
FunctionName: lambdaName, | ||
}, | ||
datapointsToAlarm: 3, | ||
evaluationPeriods: 5, | ||
metricName: "Errors", | ||
name: metricAlarmName, | ||
namespace: "AWS/Lambda", | ||
period: 60, // 1 min | ||
unit: "Seconds", | ||
statistic: "SampleCount", | ||
treatMissingData: "notBreaching", | ||
threshold: 1, | ||
}); | ||
|
||
/////////////////////////////////////////////////////////////////////////// | ||
const lambdaTargetGroup = new aws.lb.TargetGroup( | ||
`${config.name}-lambda-target-group`, | ||
{ | ||
name: `${config.name}-lambda-target-group`, | ||
targetType: "lambda", | ||
tags: { ...defaultTags, Name: `${config.name}-lambda` }, | ||
} | ||
); | ||
|
||
const withLb = new aws.lambda.Permission(`${config.name}-lambda-permission`, { | ||
action: "lambda:InvokeFunction", | ||
function: lambdaFunction.name, | ||
principal: "elasticloadbalancing.amazonaws.com", | ||
sourceArn: lambdaTargetGroup.arn, | ||
}); | ||
const lambdaTargetGroupAttachment = new aws.lb.TargetGroupAttachment( | ||
`${config.name}-lambda-target-group-attachment`, | ||
{ | ||
targetGroupArn: lambdaTargetGroup.arn, | ||
targetId: lambdaFunction.arn, | ||
}, | ||
{ | ||
dependsOn: [withLb], | ||
} | ||
); | ||
|
||
const conditions: any = [ | ||
{ | ||
pathPattern: { | ||
values: ["/embed/validate-api-key"], | ||
}, | ||
}, | ||
{ | ||
httpRequestMethod: { | ||
values: ["GET"], | ||
}, | ||
}, | ||
]; | ||
|
||
const targetPassportRule = new ListenerRule(`${config.name}-rule-lambda`, { | ||
tags: { ...defaultTags, Name: `${config.name}-rule-lambda` }, | ||
listenerArn: config.httpsListener.arn, | ||
priority: 2101, | ||
actions: [ | ||
{ | ||
type: "forward", | ||
targetGroupArn: lambdaTargetGroup.arn, | ||
}, | ||
], | ||
conditions, | ||
}); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters