Skip to content
Open
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
14 changes: 14 additions & 0 deletions .eslintrc.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
{
"parser": "@typescript-eslint/parser",
"parserOptions": {
"ecmaVersion": 2020,
"project": ["tsconfig.json"]
},
"env": {
"es2020": true
},
"extends": ["standard-with-typescript"],
"rules": {
"@typescript-eslint/ban-types": {}
}
}
19 changes: 14 additions & 5 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,5 +1,14 @@
*.tfstate.backup
*/**/dist
.terraform
functions/*/src/vendor
*/node_modules
*.js
!jest.config.js
*.d.ts
node_modules

# CDK asset staging directory
.cdk.staging
cdk.out

# Generates on `make deploy`
.aws_profile

packages
vendor
6 changes: 6 additions & 0 deletions .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
40 changes: 0 additions & 40 deletions .terraform.lock.hcl

This file was deleted.

33 changes: 33 additions & 0 deletions Makefile
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
SOURCE_DIR := lib/common/compute/lambda
DEST_DIR := packages
SUBDIRS := $(wildcard $(SOURCE_DIR)/*)
ZIP_TARGETS := $(patsubst $(SOURCE_DIR)/%,$(DEST_DIR)/%.zip,$(SUBDIRS))

CDK_DEPLOY_COMMAND := cdk deploy
AWS_PROFILE_FILE := .aws_profile

all: package
package: $(DEST_DIR) $(ZIP_TARGETS)

$(DEST_DIR):
@mkdir -p $(DEST_DIR)

$(DEST_DIR)/%.zip: $(SOURCE_DIR)/%
@echo "Packaging $<..."
@cd $< && zip -r $(abspath $@) .

clean:
@echo "Cleaning up..."
@rm -rf $(DEST_DIR)/*.zip

deploy: clean package
@if [ -f "$(AWS_PROFILE_FILE)" ]; then \
aws_profile=$$(cat $(AWS_PROFILE_FILE)); \
else \
read -p "Please enter your AWS profile name to proceed: " aws_profile; \
echo $$aws_profile > $(AWS_PROFILE_FILE); \
fi; \
aws-vault clear; \
aws-vault exec --no-session $$aws_profile -- $(CDK_DEPLOY_COMMAND); \

.PHONY: all package clean
7 changes: 7 additions & 0 deletions bin/lambda-battle.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
#!/usr/bin/env node
import "source-map-support/register";
import * as cdk from "aws-cdk-lib";
import { LambdaBattleStack } from "../lib/lambda-battle-stack";

const app = new cdk.App();
new LambdaBattleStack(app, "LambdaBattleStack");
46 changes: 46 additions & 0 deletions cdk.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
{
"app": "npx ts-node --prefer-ts-exts bin/lambda-battle.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-iam:standardizedServicePrincipals": 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
}
}
8 changes: 8 additions & 0 deletions jest.config.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
module.exports = {
testEnvironment: "node",
roots: ["<rootDir>/test"],
testMatch: ["**/*.test.ts"],
transform: {
"^.+\\.tsx?$": "ts-jest",
},
};
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
require "aws-sdk-dynamodb"
require 'json'

DB = ::Aws::DynamoDB::Client.new
LANG = "ruby-2.7-x86"
Expand Down
19 changes: 19 additions & 0 deletions lib/lambda-battle-stack.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
import * as cdk from "aws-cdk-lib";
import { type Construct } from "constructs";
import { Lambdas, ApiGateway, Database } from "./resources";

export class LambdaBattleStack extends cdk.Stack {
constructor(scope: Construct, id: string, props?: cdk.StackProps) {
super(scope, id, props);

const db = new Database(this, "Database");

const lambdas = new Lambdas(this, "Lambdas", {
baseTable: db.baseTable,
});

new ApiGateway(this, "ApiGateway", {
lambdas: lambdas.all(),
});
}
}
35 changes: 35 additions & 0 deletions lib/resources/apigateway.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
import { Construct } from "constructs";
import { type TLambdas } from "./lambdas";
import { LambdaIntegration, RestApi } from "aws-cdk-lib/aws-apigateway";
import { type IFunction } from "aws-cdk-lib/aws-lambda";

interface IApiGatewayProps {
lambdas: TLambdas;
}

export default class ApiGateway extends Construct {
private battleApi: RestApi;

constructor(scope: Construct, id: string, props: IApiGatewayProps) {
super(scope, id);

this.createLambdaBattleApi(props.lambdas);
}

private createLambdaBattleApi(funcs: TLambdas) {
this.battleApi ||= new RestApi(this, "lambda-battle-api", {
deploy: true,
restApiName: "Lambda Battle Api",
});

Object.entries(funcs).forEach(([k, w]) => {
this.addBattleLambdaEndpoint(k, w);
});
}

private addBattleLambdaEndpoint(name: string, func: IFunction) {
const lambdaIntegration = new LambdaIntegration(func);

this.battleApi.root.addResource(name).addMethod("POST", lambdaIntegration);
}
}
34 changes: 34 additions & 0 deletions lib/resources/database.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
import {
AttributeType,
BillingMode,
type ITable,
Table,
} from "aws-cdk-lib/aws-dynamodb";
import { Construct } from "constructs";

export default class Database extends Construct {
public readonly baseTable: ITable;

constructor(scope: Construct, id: string) {
super(scope, id);

this.baseTable = this.createBaseTable();
}

private createBaseTable(): ITable {
const baseTable = new Table(this, "lambda-battle-data", {
tableName: "lambda-battle-data",
partitionKey: {
name: "langCase",
type: AttributeType.STRING,
},
sortKey: {
name: "iteration",
type: AttributeType.NUMBER,
},
billingMode: BillingMode.PAY_PER_REQUEST,
});

return baseTable;
}
}
3 changes: 3 additions & 0 deletions lib/resources/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
export { default as Lambdas } from "./lambdas";
export { default as Database } from "./database";
export { default as ApiGateway } from "./apigateway";
94 changes: 94 additions & 0 deletions lib/resources/lambdas.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
import { Duration, type StackProps } from "aws-cdk-lib";
import {
Code,
Runtime,
Function,
Tracing,
type IFunction,
type FunctionOptions,
type AssetCode,
} from "aws-cdk-lib/aws-lambda";
import { Construct } from "constructs";
import { type ITable } from "aws-cdk-lib/aws-dynamodb";
import { RetentionDays } from "aws-cdk-lib/aws-logs";
import { ApiEventSource } from "aws-cdk-lib/aws-lambda-event-sources";

enum ELangCase {
Ruby2_7 = "ruby-2-7-x86",
}

interface ILambdasProps extends StackProps {
baseTable: ITable;
}

interface TCustomLambdaConfig {
runtime: Runtime;
code: AssetCode;
handler: string;
environment?: Record<string, string>;
}

export interface TLambdas {
[ELangCase.Ruby2_7]: IFunction;
}

export default class Lambdas extends Construct {
public static readonly DEFAULT_FUNCTION_PROPS: FunctionOptions = {
logRetention: RetentionDays.ONE_WEEK,
events: [ApiEventSource],
timeout: Duration.seconds(60),
tracing: Tracing.PASS_THROUGH,
};

public static readonly LAMBDA_CONFIGS = {
[ELangCase.Ruby2_7]: {
handler: "src/func.handler",
code: Code.fromAsset("./packages/ruby-2.7.zip"),
runtime: Runtime.RUBY_2_7,
environment: {
GEM_PATH: "./vendor",
},
},
};

readonly Ruby2_7Lambda: IFunction;

private readonly props: ILambdasProps;

constructor(scope: Construct, id: string, props: ILambdasProps) {
super(scope, id);

this.props = props;
this.Ruby2_7Lambda = this.createBattleLambda(ELangCase.Ruby2_7);
}

public all(): TLambdas {
return {
[ELangCase.Ruby2_7]: this.Ruby2_7Lambda,
};
}

private createBattleLambda(name: ELangCase): IFunction {
const config: TCustomLambdaConfig = Lambdas.LAMBDA_CONFIGS[name];

const battleLambdaProps = {
...Lambdas.DEFAULT_FUNCTION_PROPS,
...config,
functionName: `${name}-Battle-Function`,
environment: {
TABLE: this.props.baseTable.tableName,
...(config.environment || {}),
},
};

const battleLambda = new Function(
this,
`${name}-battle-lambda`,
battleLambdaProps
);

this.props.baseTable.grantReadWriteData(battleLambda);

return battleLambda;
}
}
Loading