Compare commits

...

32 Commits

Author SHA1 Message Date
Morgan Vernay 92848d1689 fix: only run pulumi dev github actions on staging 2023-11-15 20:39:05 +02:00
Morgan Vernay 0281a5384c fix: downgrade pulumi, faulty latest version 2023-11-15 20:05:15 +02:00
Morgan Vernay 0aff17162f fix: fix dockerfile 2023-11-15 19:01:56 +02:00
Morgan Vernay 72a31437fa fix: fix autoscaling 2023-11-15 16:12:20 +02:00
Morgan Vernay 419cce0d50 change cpu architecture 2023-11-15 14:57:47 +02:00
Morgan Vernay b6615a8fcb fix: rename github actions 2023-11-15 12:29:10 +02:00
Morgan 24343cbb68
Merge branch 'main' into deploy-api-to-serverful 2023-11-15 12:28:43 +02:00
Morgan Vernay f90bf6e741 fix: improve github actions 2023-11-15 12:26:29 +02:00
Morgan Vernay 337c74f27b test: deploy with pulumi dev api 2023-11-14 16:57:49 +02:00
Morgan Vernay e5c4764464 fix: remove extra option 2023-11-14 16:39:12 +02:00
Morgan Vernay b36bc0a41d fix: improve github action build platform 2023-11-14 16:38:23 +02:00
Morgan Vernay b5a6bb6757 fix: improve github action build platform 2023-11-14 14:21:33 +02:00
Morgan Vernay 9617cc148d feat: docker buildkit and github actions 2023-11-14 12:20:12 +02:00
Morgan 64992a90ea
Merge branch 'main' into deploy-api-to-serverful 2023-11-14 12:01:34 +02:00
Morgan 13448f59d4
Merge branch 'main' into deploy-api-to-serverful 2023-11-14 10:50:30 +02:00
Morgan 78bd0b01d1
Merge branch 'main' into deploy-api-to-serverful 2023-11-13 17:29:00 +02:00
Morgan Vernay 5b946c27ff fix: github action aws and encrypted config 2023-11-10 18:24:41 +02:00
Morgan 656f06db7e
Merge branch 'main' into deploy-api-to-serverful 2023-11-10 17:04:47 +02:00
Morgan deae23053d
Merge branch 'main' into deploy-api-to-serverful 2023-11-10 12:21:16 +02:00
Morgan Vernay f091ba234b fix preview github action 2023-11-09 19:35:57 +02:00
Morgan Vernay 8bd6d8ae47 fix preview github action 2023-11-09 19:35:29 +02:00
Morgan Vernay e02216c792 add github action 2023-11-09 19:24:55 +02:00
Morgan Vernay eabf6f7a1b add github action 2023-11-09 19:24:45 +02:00
Morgan Vernay 1b55c75266 add github action 2023-11-09 19:16:39 +02:00
Morgan bfeb1dab1e
Merge branch 'main' into deploy-api-to-serverful 2023-11-08 21:31:38 +02:00
Morgan Vernay a63878dda8 infra: deploy api to serverful 2023-11-08 18:16:10 +02:00
Morgan 914bb70a07
Merge branch 'main' into deploy-api-to-serverful 2023-11-07 11:34:28 +02:00
Morgan 01671f5ed5
Merge branch 'main' into deploy-api-to-serverful 2023-11-07 09:41:23 +02:00
Morgan Vernay 7b32f22059 refactor: put secret keys in config 2023-11-06 17:54:30 +02:00
Morgan Vernay 8ecf69fad0 refactor 2023-11-06 17:27:27 +02:00
Morgan 84e982b190
Merge branch 'main' into deploy-api-to-serverful 2023-11-06 15:08:03 +02:00
Morgan Vernay 45a8f0381c infra: docker serverful api 2023-11-06 15:07:43 +02:00
10 changed files with 3657 additions and 0 deletions

View File

@ -0,0 +1,37 @@
name: Pulumi Deploy Dev
on:
push:
branches:
- staging
jobs:
pulumi-deploy-dev:
runs-on: buildjet-4vcpu-ubuntu-2204
steps:
- name: Checkout 🛎️
uses: actions/checkout@v4
- name: Setup Node LTS ✨
uses: actions/setup-node@v3
with:
node-version: lts/*
- name: Moving to infra and Installing dependencies 📦️
run: |
cd infra
yarn install
- name: Pulumi Dev Deploy 🚀
uses: pulumi/actions@v4
with:
command: up
cloud-url: ${{ secrets.DEV_PULUMI_S3_BUCKET }}
stack-name: organization/cal-infra/${{ secrets.DEV_PULUMI_STACK_NAME }}
work-dir: ./infra
env:
PULUMI_CONFIG_PASSPHRASE: ${{ secrets.DEV_PULUMI_CONFIG_PASSPHRASE }}
AWS_ACCESS_KEY_ID: ${{ secrets.PULUMI_AWS_ACCESS_KEY_ID }}
AWS_SECRET_ACCESS_KEY: ${{ secrets.PULUMI_AWS_SECRET_ACCESS_KEY }}
AWS_REGION: ${{ secrets.PULUMI_AWS_REGION }}
NODE_ENV: development

View File

@ -0,0 +1,37 @@
name: Pulumi Preview Dev
on:
pull_request:
branches:
- staging
jobs:
pulumi-preview-dev:
runs-on: buildjet-4vcpu-ubuntu-2204
steps:
- name: Checkout 🛎️
uses: actions/checkout@v4
- name: Setup Node LTS ✨
uses: actions/setup-node@v3
with:
node-version: lts/*
- name: Moving to infra and Installing dependencies 📦️
run: |
cd infra
yarn install
- name: pulumi Dev Preview 🚀
uses: pulumi/actions@v4
with:
command: preview
cloud-url: ${{ secrets.DEV_PULUMI_S3_BUCKET }}
stack-name: organization/cal-infra/${{ secrets.DEV_PULUMI_STACK_NAME }}
work-dir: ./infra
env:
PULUMI_CONFIG_PASSPHRASE: ${{ secrets.DEV_PULUMI_CONFIG_PASSPHRASE }}
AWS_ACCESS_KEY_ID: ${{ secrets.PULUMI_AWS_ACCESS_KEY_ID }}
AWS_SECRET_ACCESS_KEY: ${{ secrets.PULUMI_AWS_SECRET_ACCESS_KEY }}
AWS_REGION: ${{ secrets.PULUMI_AWS_REGION }}
NODE_ENV: development

2
.gitignore vendored
View File

@ -96,3 +96,5 @@ apps/auth
!.yarn/releases
!.yarn/sdks
!.yarn/versions
infra/.yarn

View File

@ -0,0 +1,7 @@
encryptionsalt: v1:ohW6QUKhXRs=:v1:1j0X0vxjTVEogCe3:LTMZNRrrNn9w+qbJ0PnWZZbak+IYbg==
config:
aws:region: us-east-2
base:certificateArn:
secure: v1:G/7L0riXLzJTDWje:OA4XaOPMTT04BR8Sc2/oiues/imo5A4ZHhsP8XIEnYA8Yv2wmbFiWn0TroWd5nkr/6Wzu6wctrvSoWWSlW/oHtowoANzr5U7Wm8g/9+UO8IRm49lOcldKsZZovC/ogyGR5pw
base:secretKeys:
secure: v1:1u/79pchiw9lGfgE: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

3
infra/Pulumi.yaml Normal file
View File

@ -0,0 +1,3 @@
name: cal-infra
runtime: nodejs
description: cal.com IaC

376
infra/index.ts Normal file
View File

@ -0,0 +1,376 @@
import * as aws from "@pulumi/aws";
import type { LogGroup } from "@pulumi/aws/cloudwatch";
import type { SecurityGroup } from "@pulumi/aws/ec2";
import * as awsx from "@pulumi/awsx";
import type { Cluster } from "@pulumi/awsx/classic/ecs/cluster";
import type { Vpc } from "@pulumi/awsx/ec2/vpc";
import type { Image, Repository } from "@pulumi/awsx/ecr";
import type { FargateService } from "@pulumi/awsx/ecs";
import type { ApplicationLoadBalancer } from "@pulumi/awsx/lb";
import * as pulumi from "@pulumi/pulumi";
type SecretType = { name: string; valueFrom: string };
const awsConfig = new pulumi.Config("aws");
const baseConfig = new pulumi.Config("base");
const awsRegion = awsConfig.require("region");
const certificateArn = baseConfig.require("certificateArn");
if (!awsRegion) {
throw new Error("AWS REGION IS NOT SET");
}
// Get Secrets
const getAwsSecrets = async () => {
const secretKeys: string[] = JSON.parse(baseConfig.require("secretKeys") ?? "[]");
const SECRETS = secretKeys.map((secretKey) => {
if (process.env.NODE_ENV === "development") {
return `DEV_${secretKey}`;
}
return secretKey;
});
const res = [];
for (let index = 0; index < SECRETS.length; index++) {
try {
const secretKey = SECRETS[index];
const secret = await aws.secretsmanager.getSecret({ name: secretKey });
if (secret && secret.arn) res.push({ name: secretKey, valueFrom: secret.arn });
} catch (err) {
console.info("Secret not found:", SECRETS[index]);
}
}
return res.map((res) => ({
name: res.name.replace("DEV_", ""),
valueFrom: res.valueFrom,
}));
};
const createVpc = (name: string) => {
// Create VPC
const vpc = new awsx.ec2.Vpc(name, {
cidrBlock: "10.0.0.0/16",
});
return vpc;
};
const createHttpsSecurityGroup = (name: string, vpcId: Vpc["vpcId"]) => {
// Create Security Group
const sg = new aws.ec2.SecurityGroup(name, {
vpcId: vpcId,
ingress: [
{
description: "allow HTTP access from anywhere",
fromPort: 80,
toPort: 80,
protocol: "tcp",
cidrBlocks: ["0.0.0.0/0"],
},
{
description: "allow HTTPS access from anywhere",
fromPort: 443,
toPort: 443,
protocol: "tcp",
cidrBlocks: ["0.0.0.0/0"],
},
],
egress: [
{
fromPort: 0,
toPort: 0,
protocol: "-1",
cidrBlocks: ["0.0.0.0/0"],
},
],
});
return sg;
};
const createLog = (name: string) => {
// Create Cloudwatch LogGroup and Stream
const logGroup = new aws.cloudwatch.LogGroup(`${name}-log-group`);
const logStream = new aws.cloudwatch.LogStream(`${name}-log-stream`, {
logGroupName: logGroup.name,
});
return { logGroup, logStream };
};
const createAppLoadBalancer = (
name: string,
publicSubnetIds: Vpc["publicSubnetIds"],
securityGroupId: SecurityGroup["id"]
) => {
// Create Application Load Balancer
const lb = new awsx.lb.ApplicationLoadBalancer(name, {
securityGroups: [securityGroupId],
subnetIds: publicSubnetIds,
defaultTargetGroup: { healthCheck: { matcher: "200-299" }, port: 80, protocol: "HTTP" },
listeners: [
{
port: 80,
protocol: "HTTP",
defaultActions: [
{
type: "redirect",
redirect: {
protocol: "HTTPS",
port: "443",
statusCode: "HTTP_301",
},
},
],
},
{
port: 443,
protocol: "HTTPS",
certificateArn: certificateArn,
},
],
});
return lb;
};
const createDockerImagesRepo = (name: string) => {
// Create ECR Image Repository
const repository = new awsx.ecr.Repository(name, {});
return repository;
};
const createDockerImage = ({
imageName,
repoUrl,
dockerFilePath,
buildContextPath,
}: {
imageName: string;
repoUrl: Repository["url"];
dockerFilePath: string;
buildContextPath: string;
}) => {
// Create Docker Image of Api and Store in Repo
const image = new awsx.ecr.Image(`${imageName}:latest`, {
repositoryUrl: repoUrl,
dockerfile: dockerFilePath,
context: buildContextPath,
cacheFrom: [pulumi.interpolate`${repoUrl}:latest`],
platform: "linux/amd64",
});
return image;
};
const createElasticContainerCluster = (clusterName: string) => {
// Create ECS cluster
const cluster = new awsx.classic.ecs.Cluster(clusterName, {});
return cluster;
};
const createFargateServiceWithSecrets = ({
secrets,
imageUri,
logGroupName,
loadBalancerTargetGroup,
ecsClusterArn,
privateSubnetIds,
securityGroupId,
serviceName,
desiredNbOfTasks,
cpu,
memory,
}: {
secrets: SecretType[];
imageUri: Image["imageUri"];
logGroupName: LogGroup["name"];
loadBalancerTargetGroup: ApplicationLoadBalancer["defaultTargetGroup"];
ecsClusterArn: Cluster["cluster"]["arn"];
privateSubnetIds: Vpc["privateSubnetIds"];
securityGroupId: SecurityGroup["id"];
serviceName: string;
desiredNbOfTasks: number;
cpu: 512 | 1024 | 2048;
memory: 1000 | 2000 | 3000 | 4000;
}) => {
// Policy For Secrets
const secretsManagerAccessPolicy = new aws.iam.Policy(`${serviceName}-fargate-secrets-policy`, {
policy: {
Version: "2012-10-17",
Statement: [
{
Effect: "Allow",
Action: "secretsmanager:GetSecretValue",
Resource: "*",
},
{
Effect: "Allow",
Action: [
"ecr:GetAuthorizationToken",
"ecr:BatchCheckLayerAvailability",
"ecr:GetDownloadUrlForLayer",
"ecr:BatchGetImage",
"logs:CreateLogStream",
"logs:PutLogEvents",
],
Resource: "*",
},
],
},
});
// IAM Role To Attach To Fargate Service for Accessing Secrets
const taskRole = new aws.iam.Role("task-exec-role", {
assumeRolePolicy: {
Version: "2012-10-17",
Statement: [
{
Action: "sts:AssumeRole",
Principal: {
Service: "ecs-tasks.amazonaws.com",
},
Effect: "Allow",
},
],
},
});
// Attach Policy and Role
new aws.iam.RolePolicyAttachment(`${serviceName}-task-exec-policy-attach`, {
role: taskRole,
policyArn: secretsManagerAccessPolicy.arn,
});
// Create Fargate Service
const service = new awsx.ecs.FargateService(`${serviceName}-fargate-service`, {
cluster: ecsClusterArn,
networkConfiguration: {
subnets: privateSubnetIds,
securityGroups: [securityGroupId],
assignPublicIp: true,
},
desiredCount: desiredNbOfTasks,
taskDefinitionArgs: {
executionRole: { roleArn: taskRole.arn },
logGroup: { skip: true },
runtimePlatform: {
cpuArchitecture: "X86_64",
},
container: {
name: serviceName,
image: imageUri,
cpu: cpu,
memory: memory,
essential: true,
portMappings: [
{
containerPort: 80,
hostPort: 80,
targetGroup: loadBalancerTargetGroup,
},
],
logConfiguration: {
logDriver: "awslogs",
options: {
"awslogs-group": logGroupName,
"awslogs-stream-prefix": serviceName,
"awslogs-region": `${awsRegion}`,
},
},
secrets: secrets ?? [],
},
},
});
return service;
};
const createAutoScalingCpu = ({
name,
ecsClusterName,
serviceName,
cpuTargetValue,
maxCapacity,
minCapacity,
scaleInCooldown,
scaleOutCooldown,
}: {
name: string;
ecsClusterName: Cluster["cluster"]["name"];
serviceName: FargateService["service"]["name"];
cpuTargetValue: number;
maxCapacity: number;
minCapacity: number;
scaleInCooldown: number;
scaleOutCooldown: number;
}) => {
// Create Autoscaling for the ECS service, Scale when CPU > 75%
const autoscaling = new aws.appautoscaling.Policy(name, {
serviceNamespace: "ecs",
scalableDimension: "ecs:service:DesiredCount",
resourceId: pulumi.interpolate`service/${ecsClusterName}/${serviceName}`,
policyType: "TargetTrackingScaling",
targetTrackingScalingPolicyConfiguration: {
targetValue: cpuTargetValue,
predefinedMetricSpecification: {
predefinedMetricType: "ECSServiceAverageCPUUtilization",
},
scaleInCooldown,
scaleOutCooldown,
},
});
// Set Min and Max Number of Tasks
const autoscalingTarget = new aws.appautoscaling.Target(`${name}-scaling-target`, {
maxCapacity: maxCapacity, // maximum number of tasks
minCapacity: minCapacity, // minimum number of tasks
resourceId: pulumi.interpolate`service/${ecsClusterName}/${serviceName}`,
scalableDimension: "ecs:service:DesiredCount",
serviceNamespace: "ecs",
});
return { autoscaling, autoscalingTarget };
};
const addSuffixToName = (name: string) => {
const suffix = process.env.NODE_ENV === "development" ? "dev" : "prod";
return `${name}-${suffix}`;
};
const main = async () => {
const awsSecrets = await getAwsSecrets();
const vpc = createVpc(addSuffixToName("cal-api-vpc"));
const httpsSg = createHttpsSecurityGroup(addSuffixToName("cal-api-sg"), vpc.vpcId);
const apiAlb = createAppLoadBalancer(addSuffixToName("cal-api-lb"), vpc.publicSubnetIds, httpsSg.id);
const { logGroup } = createLog(addSuffixToName("cal-api"));
const repo = createDockerImagesRepo(addSuffixToName("cal-api-repo"));
const apiImage = createDockerImage({
imageName: addSuffixToName("cal-api-image"),
repoUrl: repo.url,
dockerFilePath: "./docker/api/Dockerfile",
buildContextPath: "../",
});
const ecsCluster = createElasticContainerCluster(addSuffixToName("cal-api-cluster"));
const apiService = createFargateServiceWithSecrets({
desiredNbOfTasks: 2,
privateSubnetIds: vpc.privateSubnetIds,
securityGroupId: httpsSg.id,
secrets: awsSecrets,
imageUri: apiImage.imageUri,
logGroupName: logGroup.name,
loadBalancerTargetGroup: apiAlb.defaultTargetGroup,
ecsClusterArn: ecsCluster.cluster.arn,
serviceName: addSuffixToName("cal-api-fargate"),
cpu: 1024,
memory: 2000,
});
const _ = createAutoScalingCpu({
name: addSuffixToName("cal-api-cpu-scaling"),
ecsClusterName: ecsCluster.cluster.name,
serviceName: apiService.service.name,
minCapacity: 1,
maxCapacity: 3,
cpuTargetValue: 75,
scaleInCooldown: 120,
scaleOutCooldown: 60,
});
return { apiDnsName: pulumi.interpolate`apiUrl: ${apiAlb.loadBalancer.dnsName}` };
};
main().then(({ apiDnsName }) => {
console.info(apiDnsName);
});

16
infra/package.json Normal file
View File

@ -0,0 +1,16 @@
{
"name": "@calcom/infra",
"main": "index.ts",
"scripts": {
"deploy-dev": "NODE_ENV=development pulumi up",
"deploy-prod": "NODE_ENV=production pulumi up"
},
"devDependencies": {
"@types/node": "^20.9.0"
},
"dependencies": {
"@pulumi/aws": "^6.0.0",
"@pulumi/awsx": "^2.0.2",
"@pulumi/pulumi": "^3.0.0"
}
}

16
infra/tsconfig.json Normal file
View File

@ -0,0 +1,16 @@
{
"compilerOptions": {
"strict": true,
"outDir": "bin",
"target": "es2016",
"module": "commonjs",
"moduleResolution": "node",
"sourceMap": true,
"experimentalDecorators": true,
"pretty": true,
"noFallthroughCasesInSwitch": true,
"noImplicitReturns": true,
"forceConsistentCasingInFileNames": true
},
"files": ["index.ts"]
}

3159
infra/yarn.lock Normal file

File diff suppressed because it is too large Load Diff

View File

@ -47,6 +47,10 @@
"cache": false,
"dependsOn": []
},
"@calcom/api#start": {
"cache": false,
"dependsOn": []
},
"@calcom/ai#build": {
"env": [
"FRONTEND_URL",