Skip to content
Merged

use cdk #1768

Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 5 additions & 14 deletions .github/workflows/aws-deploy.yml
Original file line number Diff line number Diff line change
Expand Up @@ -55,8 +55,8 @@ jobs:
- name: install dependencies
run: |
cd ./result-builder && yarn install
- name: install Serverless Framework
run: sudo yarn global add serverless@^4.4.14 --prefix /usr/local
- name: install CDK
run: sudo yarn global add aws-cdk
- name: configure aws credentials
uses: aws-actions/configure-aws-credentials@v4.0.2
with:
Expand All @@ -66,17 +66,8 @@ jobs:
- name: deploy
env:
AWS_REGION: ${{ inputs.region }}
GH_AUTH_TOKEN: ${{ secrets.GH_AUTH_TOKEN }}
LAMBDA_PERF_ENV: ${{ inputs.lambdaPerfEnv }}
SKIP_SNAPSTART: ${{ (inputs.region == 'eu-west-1') && secrets.SKIP_SNAPSTART_DEV || 'false' }}
SERVERLESS_ACCESS_KEY: ${{ secrets.SERVERLESS_ACCESS_KEY }}
run: sls deploy --force
- name: Remove the cron job scheduler in staging (eu-west-1)
if: "${{ inputs.region == 'eu-west-1' }}"
run: |
RULE_NAME=$(aws events list-rule-names-by-target --target-arn arn:aws:lambda:${{ inputs.region }}:${{ secrets.AWS_ACCOUNT_ID }}:function:lambda-perf-dev-functionTriggerDeployerRs --region ${{ inputs.region }} | jq -r '.RuleNames[0]')
echo "Disabling rule ${RULE_NAME}"
aws events disable-rule --name $RULE_NAME --region ${{ inputs.region }}
RULE_NAME=$(aws events list-rule-names-by-target --target-arn arn:aws:lambda:${{ inputs.region }}:${{ secrets.AWS_ACCOUNT_ID }}:function:lambda-perf-dev-resultBuilder --region ${{ inputs.region }} | jq -r '.RuleNames[0]')
echo "Disabling rule ${RULE_NAME}"
aws events disable-rule --name $RULE_NAME --region ${{ inputs.region }}
GITHUB_AUTH_TOKEN: ${{ secrets.GH_AUTH_TOKEN }}
AWS_ACCOUNT_ID: ${{ secrets.AWS_ACCOUNT_ID }}
run: cd cdk && yarn install && cdk bootstrap && cdk deploy LambdaPerfStack --require-approval never
8 changes: 8 additions & 0 deletions cdk/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
*.js
!jest.config.js
*.d.ts
node_modules

# CDK asset staging directory
.cdk.staging
cdk.out
6 changes: 6 additions & 0 deletions cdk/.npmignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
*.ts
!*.d.ts

# CDK asset staging directory
.cdk.staging
cdk.out
16 changes: 16 additions & 0 deletions cdk/bin/cdk.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
import * as cdk from 'aws-cdk-lib';
import { LambdaPerfStack } from '../lib/lambda-perf-stack';

const requiredEnvVars = ['AWS_ACCOUNT_ID', 'AWS_REGION', 'SKIP_SNAPSTART', 'GITHUB_AUTH_TOKEN', 'LAMBDA_PERF_ENV'];
const missingEnvVars = requiredEnvVars.filter(envVar => !process.env[envVar]);
if (missingEnvVars.length > 0) {
throw new Error(`Missing required environment variables: ${missingEnvVars.join(', ')}`);
}

const app = new cdk.App();
new LambdaPerfStack(app, 'LambdaPerfStack', {
env: { account: process.env.AWS_ACCOUNT_ID, region: process.env.AWS_REGION },
skipSnapstart: process.env.SKIP_SNAPSTART!!,
githubAuthToken: process.env.GITHUB_AUTH_TOKEN!!,
lambdaPerfEnv: process.env.LAMBDA_PERF_ENV!!,
});
84 changes: 84 additions & 0 deletions cdk/cdk.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
{
"app": "npx ts-node --prefer-ts-exts bin/cdk.ts",
"watch": {
"include": [
"**"
],
"exclude": [
"README.md",
"cdk*.json",
"**/*.d.ts",
"**/*.js",
"tsconfig.json",
"package*.json",
"yarn.lock",
"node_modules",
"test"
]
},
"context": {
"@aws-cdk/aws-lambda:recognizeLayerVersion": true,
"@aws-cdk/core:checkSecretUsage": true,
"@aws-cdk/core:target-partitions": [
"aws",
"aws-cn"
],
"@aws-cdk-containers/ecs-service-extensions:enableDefaultLogDriver": true,
"@aws-cdk/aws-ec2:uniqueImdsv2TemplateName": true,
"@aws-cdk/aws-ecs:arnFormatIncludesClusterName": true,
"@aws-cdk/aws-iam:minimizePolicies": true,
"@aws-cdk/core:validateSnapshotRemovalPolicy": true,
"@aws-cdk/aws-codepipeline:crossAccountKeyAliasStackSafeResourceName": true,
"@aws-cdk/aws-s3:createDefaultLoggingPolicy": true,
"@aws-cdk/aws-sns-subscriptions:restrictSqsDescryption": true,
"@aws-cdk/aws-apigateway:disableCloudWatchRole": true,
"@aws-cdk/core:enablePartitionLiterals": true,
"@aws-cdk/aws-events:eventsTargetQueueSameAccount": true,
"@aws-cdk/aws-ecs:disableExplicitDeploymentControllerForCircuitBreaker": true,
"@aws-cdk/aws-iam:importedRoleStackSafeDefaultPolicyName": true,
"@aws-cdk/aws-s3:serverAccessLogsUseBucketPolicy": true,
"@aws-cdk/aws-route53-patters:useCertificate": true,
"@aws-cdk/customresources:installLatestAwsSdkDefault": false,
"@aws-cdk/aws-rds:databaseProxyUniqueResourceName": true,
"@aws-cdk/aws-codedeploy:removeAlarmsFromDeploymentGroup": true,
"@aws-cdk/aws-apigateway:authorizerChangeDeploymentLogicalId": true,
"@aws-cdk/aws-ec2:launchTemplateDefaultUserData": true,
"@aws-cdk/aws-secretsmanager:useAttachedSecretResourcePolicyForSecretTargetAttachments": true,
"@aws-cdk/aws-redshift:columnId": true,
"@aws-cdk/aws-stepfunctions-tasks:enableEmrServicePolicyV2": true,
"@aws-cdk/aws-ec2:restrictDefaultSecurityGroup": true,
"@aws-cdk/aws-apigateway:requestValidatorUniqueId": true,
"@aws-cdk/aws-kms:aliasNameRef": true,
"@aws-cdk/aws-autoscaling:generateLaunchTemplateInsteadOfLaunchConfig": true,
"@aws-cdk/core:includePrefixInUniqueNameGeneration": true,
"@aws-cdk/aws-efs:denyAnonymousAccess": true,
"@aws-cdk/aws-opensearchservice:enableOpensearchMultiAzWithStandby": true,
"@aws-cdk/aws-lambda-nodejs:useLatestRuntimeVersion": true,
"@aws-cdk/aws-efs:mountTargetOrderInsensitiveLogicalId": true,
"@aws-cdk/aws-rds:auroraClusterChangeScopeOfInstanceParameterGroupWithEachParameters": true,
"@aws-cdk/aws-appsync:useArnForSourceApiAssociationIdentifier": true,
"@aws-cdk/aws-rds:preventRenderingDeprecatedCredentials": true,
"@aws-cdk/aws-codepipeline-actions:useNewDefaultBranchForCodeCommitSource": true,
"@aws-cdk/aws-cloudwatch-actions:changeLambdaPermissionLogicalIdForLambdaAction": true,
"@aws-cdk/aws-codepipeline:crossAccountKeysDefaultValueToFalse": true,
"@aws-cdk/aws-codepipeline:defaultPipelineTypeToV2": true,
"@aws-cdk/aws-kms:reduceCrossAccountRegionPolicyScope": true,
"@aws-cdk/aws-eks:nodegroupNameAttribute": true,
"@aws-cdk/aws-ec2:ebsDefaultGp3Volume": true,
"@aws-cdk/aws-ecs:removeDefaultDeploymentAlarm": true,
"@aws-cdk/custom-resources:logApiResponseDataPropertyTrueDefault": false,
"@aws-cdk/aws-s3:keepNotificationInImportedBucket": false,
"@aws-cdk/aws-ecs:enableImdsBlockingDeprecatedFeature": false,
"@aws-cdk/aws-ecs:disableEcsImdsBlocking": true,
"@aws-cdk/aws-ecs:reduceEc2FargateCloudWatchPermissions": true,
"@aws-cdk/aws-dynamodb:resourcePolicyPerReplica": true,
"@aws-cdk/aws-ec2:ec2SumTImeoutEnabled": true,
"@aws-cdk/aws-appsync:appSyncGraphQLAPIScopeLambdaPermission": true,
"@aws-cdk/aws-rds:setCorrectValueForDatabaseInstanceReadReplicaInstanceResourceId": true,
"@aws-cdk/core:cfnIncludeRejectComplexResourceUpdateCreatePolicyIntrinsics": true,
"@aws-cdk/aws-lambda-nodejs:sdkV3ExcludeSmithyPackages": true,
"@aws-cdk/aws-stepfunctions-tasks:fixRunEcsTaskPolicy": true,
"@aws-cdk/aws-ec2:bastionHostUseAmazonLinux2023ByDefault": true,
"@aws-cdk/aws-route53-targets:userPoolDomainNameMethodWithoutCustomResource": true
}
}
181 changes: 181 additions & 0 deletions cdk/lib/lambda-perf-stack.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,181 @@
import * as cdk from 'aws-cdk-lib';
import { Stack, StackProps } from 'aws-cdk-lib';
import * as lambda from 'aws-cdk-lib/aws-lambda';
import * as s3 from 'aws-cdk-lib/aws-s3';
import * as sqs from 'aws-cdk-lib/aws-sqs';
import * as events from 'aws-cdk-lib/aws-events';
import * as targets from 'aws-cdk-lib/aws-events-targets';
import * as iam from 'aws-cdk-lib/aws-iam';
import { Construct } from 'constructs';
import { createRole } from './role';
import * as lambdaEventSources from 'aws-cdk-lib/aws-lambda-event-sources';

interface LambdaPerfStackProps extends cdk.StackProps {
skipSnapstart: string;
githubAuthToken: string;
lambdaPerfEnv: string;
}

export class LambdaPerfStack extends Stack {
constructor(scope: Construct, id: string, props: LambdaPerfStackProps) {
super(scope, id, props);

const region = this.region;
const accountId = this.account;

// S3 Deployment Bucket
new s3.Bucket(this, 'LambdaPerfBucket', {
bucketName: `lambda-perf-${region}`,
});

const lambdaRoleArn = createRole(this);

// SQS Queues
const deployerQueue = new sqs.Queue(this, 'LambdaPerfDeployerQueue', {
queueName: 'lambda-perf-deployer',
visibilityTimeout: cdk.Duration.seconds(900),
});

const snapstartDeployerQueue = new sqs.Queue(this, 'LambdaPerfSnapstartDeployerQueue', {
queueName: 'lambda-perf-snapstart-deployer',
visibilityTimeout: cdk.Duration.seconds(900),
});

const invokerQueue = new sqs.Queue(this, 'LambdaPerfInvokerQueue', {
queueName: 'lambda-perf-invoker',
visibilityTimeout: cdk.Duration.seconds(900),
});

// Lambda Functions
const functionReportLogProcessorRs = new lambda.Function(this, 'FunctionReportLogProcessorRs', {
runtime: lambda.Runtime.PROVIDED_AL2023,
architecture: lambda.Architecture.ARM_64,
handler: 'bootstrap',
code: lambda.Code.fromAsset('../target/lambda/function-report-log-processor-rs/bootstrap-function-report-log-processor-rs.zip'),
environment: { TABLE_NAME: 'report-log' },
memorySize: 128,
timeout: cdk.Duration.seconds(900),
role: iam.Role.fromRoleArn(this, 'RoleFunctionReportLogProcessorRs', lambdaRoleArn),
});

const functionTriggerDeployerRs = new lambda.Function(this, 'FunctionTriggerDeployerRs', {
runtime: lambda.Runtime.PROVIDED_AL2023,
architecture: lambda.Architecture.ARM_64,
handler: 'bootstrap',
code: lambda.Code.fromAsset('../target/lambda/function-trigger-deployer-rs/bootstrap-function-trigger-deployer-rs.zip'),
environment: {
TABLE_NAME: 'report-log',
REPORT_LOG_PROCESSOR_ARN: functionReportLogProcessorRs.functionArn,
FUNCTION_QUEUE_NAME: deployerQueue.queueName,
SNAPSTART_QUEUE_NAME: snapstartDeployerQueue.queueName,
SKIP_SNAPSTART: props.skipSnapstart,
ACCOUNT_ID: accountId,
},
timeout: cdk.Duration.seconds(60),
role: iam.Role.fromRoleArn(this, 'RoleFunctionTriggerDeployerRs', lambdaRoleArn),
});


if (props.lambdaPerfEnv === 'production') {
new events.Rule(this, 'FunctionTriggerDeployerSchedule', {
schedule: events.Schedule.cron({ minute: '0', hour: '11', day: '*', month: '*', year: '*' }),
targets: [new targets.LambdaFunction(functionTriggerDeployerRs)],
});
}

const functionDeployerRs = new lambda.Function(this, 'FunctionDeployerRs', {
runtime: lambda.Runtime.PROVIDED_AL2023,
architecture: lambda.Architecture.ARM_64,
handler: 'bootstrap',
code: lambda.Code.fromAsset('../target/lambda/function-deployer-rs/bootstrap-function-deployer-rs.zip'),
environment: {
ACCOUNT_ID: accountId,
REPORT_LOG_PROCESSOR_ARN: functionReportLogProcessorRs.functionArn,
ROLE_ARN: lambdaRoleArn,
INVOKER_QUEUE_NAME: invokerQueue.queueName,
},
timeout: cdk.Duration.seconds(900),
role: iam.Role.fromRoleArn(this, 'RoleFunctionDeployerRs', lambdaRoleArn),
});

deployerQueue.grantSendMessages(functionDeployerRs);

functionDeployerRs.addEventSource(
new lambdaEventSources.SqsEventSource(deployerQueue, {
batchSize: 5,
maxBatchingWindow: cdk.Duration.seconds(30),
})
);

const functionSnapstartDeployerRs = new lambda.Function(this, 'FunctionSnapstartDeployerRs', {
runtime: lambda.Runtime.PROVIDED_AL2023,
architecture: lambda.Architecture.ARM_64,
handler: 'bootstrap',
code: lambda.Code.fromAsset('../target/lambda/function-deployer-rs/bootstrap-function-deployer-rs.zip'),
environment: {
ACCOUNT_ID: accountId,
REPORT_LOG_PROCESSOR_ARN: functionReportLogProcessorRs.functionArn,
ROLE_ARN: lambdaRoleArn,
INVOKER_QUEUE_NAME: invokerQueue.queueName,
},
timeout: cdk.Duration.seconds(900),
role: iam.Role.fromRoleArn(this, 'RoleFunctionSnapstartDeployerRs', lambdaRoleArn),
});

snapstartDeployerQueue.grantSendMessages(functionSnapstartDeployerRs);

functionSnapstartDeployerRs.addEventSource(
new lambdaEventSources.SqsEventSource(snapstartDeployerQueue, {
batchSize: 2,
maxBatchingWindow: cdk.Duration.seconds(120),
maxConcurrency: 2,
})
);

const functionInvokerRs = new lambda.Function(this, 'FunctionInvokerRs', {
runtime: lambda.Runtime.PROVIDED_AL2023,
architecture: lambda.Architecture.ARM_64,
handler: 'bootstrap',
code: lambda.Code.fromAsset('../target/lambda/function-invoker-rs/bootstrap-function-invoker-rs.zip'),
environment: {
ACCOUNT_ID: cdk.Aws.ACCOUNT_ID,
ROLE_ARN: lambdaRoleArn,
},
timeout: cdk.Duration.seconds(900),
role: iam.Role.fromRoleArn(this, 'RoleFunctionInvokerRs', lambdaRoleArn),
});

invokerQueue.grantConsumeMessages(functionInvokerRs);

functionInvokerRs.addEventSource(
new lambdaEventSources.SqsEventSource(invokerQueue, {
batchSize: 5,
maxBatchingWindow: cdk.Duration.seconds(30),
})
);

const resultBuilder = new lambda.Function(this, 'ResultBuilder', {
runtime: lambda.Runtime.NODEJS_22_X,
handler: 'app.handler',
code: lambda.Code.fromAsset('../result-builder'),
environment: {
GH_AUTH_TOKEN: props.githubAuthToken,
LAMBDA_PERF_ENV: props.lambdaPerfEnv,
},
timeout: cdk.Duration.seconds(60),
role: iam.Role.fromRoleArn(this, 'RoleFunctionResultBuilder', lambdaRoleArn),
});

const rule = new events.Rule(this, 'ScheduleRule', {
schedule: events.Schedule.cron({
minute: '0',
hour: '14',
day: '*',
month: '*',
})
});

// Add the Lambda function as a target for the scheduled rule
rule.addTarget(new targets.LambdaFunction(resultBuilder));
}
}
Loading
Loading