Skip to content
Closed
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
6 changes: 3 additions & 3 deletions efimeral/lambdas/api/check-box-id.js
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ exports.handler = Sentry.AWSLambda.wrapHandler(async (event, context) => {
};
}

const containerURL = await getContainerURL(task.attachments[0].details);
const containerURL = await getContainerURL(task.attachments[0].details, task.containers[0].networkBindings[0].containerPort);
return {
statusCode: 200,
headers: headers,
Expand Down Expand Up @@ -73,7 +73,7 @@ async function getRunningTaskById(clusterArn, taskId, ecs) {
return undefined;
}

async function getContainerURL(details) {
async function getContainerURL(details, containerPort) {
let eni = '';
for (let i = 0; i < details.length; i++) {
if (details[i].name === 'networkInterfaceId') {
Expand All @@ -94,5 +94,5 @@ async function getContainerURL(details) {
const data = await ec2.describeNetworkInterfaces(params);
console.log(`Network data: ${JSON.stringify(data)}`);

return `http://${data.NetworkInterfaces[0].Association.PublicDnsName}:${process.env.CONTAINER_PORT}`
return `http://${data.NetworkInterfaces[0].Association.PublicDnsName}:${containerPort}`
}
41 changes: 28 additions & 13 deletions efimeral/lib/efimeral-stack.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,13 +14,20 @@ import * as secretsmanager from 'aws-cdk-lib/aws-secretsmanager';
import * as route53 from 'aws-cdk-lib/aws-route53';
import * as route53Targets from 'aws-cdk-lib/aws-route53-targets';
import * as acm from 'aws-cdk-lib/aws-certificatemanager';
import * as crypto from 'crypto';


export const ecrRepositoyName = 'efimeral-boxes';
export const containerPort = 8080;
export const containerTimeoutMinutes = 10;
export const apiSubdomain = 'api.efimeral.ar';
export const imageTags = ['alpine', 'ubuntu']
export const images = [
{tag: 'alpine', ecr: true, port: 8080},
{tag: 'ubuntu', ecr: true, port: 8080},
{tag: 'vscode', ecr: false, port: 8000, image: 'ahmadnassri/vscode-server', environment: {
VSCODE_SERVE_MODE: 'serve-local',
VSCODE_KEYRING_PASS: crypto.randomBytes(20).toString('hex'),
}}
]

export class APIStack extends cdk.Stack {
constructor(scope: Construct, id: string, props?: cdk.StackProps) {
Expand Down Expand Up @@ -64,30 +71,39 @@ export class APIStack extends cdk.Stack {

const tasks: { [key: string]: ecs.TaskDefinition } = {};
const taskArns: { [key: string]: string} = {};
imageTags.forEach(tag => {
const task = new ecs.TaskDefinition(this, `box-task-${tag}`, {
images.forEach(tag => {
const task = new ecs.TaskDefinition(this, `box-task-${tag.tag}`, {
compatibility: ecs.Compatibility.FARGATE,
cpu: '256',
memoryMiB: '512',
});
task.addContainer(`box-${tag}`, {
image: ecs.ContainerImage.fromEcrRepository(repository, tag),

let image;
if (tag.ecr) {
image = ecs.ContainerImage.fromEcrRepository(repository, tag.tag);
} else {
image = ecs.ContainerImage.fromRegistry(`${tag.image}:latest`);
}

task.addContainer(`box-${tag.tag}`, {
image: image,
cpu: 1,
memoryReservationMiB: 512,
portMappings: [
{
containerPort: containerPort,
containerPort: tag.port,
protocol: ecs.Protocol.TCP,
},
],
logging: ecs.LogDrivers.awsLogs({
streamPrefix: `box-${tag}`,
streamPrefix: `box-${tag.tag}`,
logRetention: logs.RetentionDays.ONE_WEEK,
}),
environment: tag.environment,
});

tasks[tag] = task;
taskArns[tag] = task.taskDefinitionArn;
tasks[tag.tag] = task;
taskArns[tag.tag] = task.taskDefinitionArn;
});

const sentryDSN = secretsmanager.Secret.fromSecretNameV2(
Expand All @@ -109,7 +125,7 @@ export class APIStack extends cdk.Stack {
MAX_ALLOWED_RUNNING_TASKS: "10",
TASK_DEFINITION_ARNS: JSON.stringify(taskArns),
DEFAULT_TAG: 'alpine',
AVAILABLE_TAGS: JSON.stringify(imageTags),
AVAILABLE_TAGS: JSON.stringify(images.map(tag => tag.tag)),
SUBNET_ID: vpc.publicSubnets[0].subnetId,
SECURITY_GROUP_ID: sg.securityGroupId,
},
Expand All @@ -123,7 +139,7 @@ export class APIStack extends cdk.Stack {
},
});

imageTags.forEach(tag => tasks[tag].grantRun(fnApiCreateBoxHandler));
images.forEach(tag => tasks[tag.tag].grantRun(fnApiCreateBoxHandler));

const fnApiCheckBoxIdHandler = new lambdaNodeJS.NodejsFunction(this, 'api-check-box-id', {
description: 'Checks RUNNING state for box ID and returns its public URL if exists',
Expand All @@ -137,7 +153,6 @@ export class APIStack extends cdk.Stack {
LAMBDAS_SENTRY_DSN: sentryDSN,
CORS_DISABLED: "true",
CLUSTER_ARN: cluster.clusterArn,
CONTAINER_PORT: `${containerPort}`,
},
bundling: {
esbuildArgs: {
Expand Down
26 changes: 20 additions & 6 deletions efimeral/package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

8 changes: 5 additions & 3 deletions efimeral/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -15,9 +15,10 @@
"@aws-sdk/client-ec2": "^3.332.0",
"@aws-sdk/client-ecs": "^3.338.0",
"@types/jest": "^27.5.2",
"@types/node": "10.17.27",
"@types/prettier": "2.6.0",
"aws-cdk": "2.80.0",
"@types/mocha": "^10.0.1",
"@types/node": "^10.17.27",
"@types/prettier": "2.7.3",
"aws-cdk": "^2.80.0",
"aws-sdk-client-mock": "^2.1.1",
"esbuild": "^0.17.19",
"jest": "^27.5.1",
Expand All @@ -30,6 +31,7 @@
"adm-zip": "^0.5.10",
"aws-cdk-lib": "2.80.0",
"constructs": "^10.0.0",
"crypto": "^1.0.1",
"source-map-support": "^0.5.21"
}
}
6 changes: 3 additions & 3 deletions efimeral/test/efimeral.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -84,18 +84,18 @@ test('Stack created', () => {
MaxSize: '3',
});

Efimeral.imageTags.forEach(tag => {
Efimeral.images.forEach(tag => {
template.hasResourceProperties('AWS::ECS::TaskDefinition', {
Cpu: '256',
Memory: '512',
RequiresCompatibilities: ['FARGATE'],
ContainerDefinitions: [{
Name: `box-${tag}`,
Name: `box-${tag.tag}`,
Cpu: 1,
Essential: true,
MemoryReservation: 512,
PortMappings: [{
ContainerPort: 8080,
ContainerPort: tag.port,
Protocol: ecs.Protocol.TCP,
}],
}],
Expand Down
10 changes: 10 additions & 0 deletions efimeral/test/lambda-api-check-box-id.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,11 @@ describe("Check box running state by ID", () => {
value: 'fakeNetworkId',
}]
}],
containers: [{
networkBindings: [{
containerPort: 8080,
}]
}],
lastStatus: 'RUNNING',
desiredStatus: 'RUNNING',
}],
Expand Down Expand Up @@ -82,6 +87,11 @@ describe("Check box running state by ID", () => {
attachments: [{
details: []
}],
containers: [{
networkBindings: [{
containerPort: 8080,
}]
}],
lastStatus: 'RUNNING',
desiredStatus: 'RUNNING',
}],
Expand Down
3 changes: 2 additions & 1 deletion efimeral/test/lambda-api-create-box.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ const { mockClient } = require("aws-sdk-client-mock");
const lambdaFunction = require("../lambdas/api/create-box");

const ecsMock = mockClient(ECSClient);
const validTags = ["alpine", "ubuntu"];
const validTags = ["alpine", "ubuntu", "vscode"];

describe("Create box", () => {
const ENV = process.env;
Expand All @@ -16,6 +16,7 @@ describe("Create box", () => {
process.env.TASK_DEFINITION_ARNS = JSON.stringify({
alpine: 'fakeArn1',
ubuntu: 'fakeArn2',
vscode: 'fakeArn3'
});
});

Expand Down
2 changes: 1 addition & 1 deletion package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.