limgen
Version:
Infrastructure as Code generator
298 lines (297 loc) • 10.9 kB
JavaScript
"use strict";
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
var desc = Object.getOwnPropertyDescriptor(m, k);
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
desc = { enumerable: true, get: function() { return m[k]; } };
}
Object.defineProperty(o, k2, desc);
}) : (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
o[k2] = m[k];
}));
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
Object.defineProperty(o, "default", { enumerable: true, value: v });
}) : function(o, v) {
o["default"] = v;
});
var __importStar = (this && this.__importStar) || function (mod) {
if (mod && mod.__esModule) return mod;
var result = {};
if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
__setModuleDefault(result, mod);
return result;
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.AppFargate = exports.defaultFullstackServiceAWSArgs = void 0;
const pulumi = __importStar(require("@pulumi/pulumi"));
const aws = __importStar(require("@pulumi/aws"));
const awsx = __importStar(require("@pulumi/awsx"));
const prefixed_1 = require("../utils/prefixed");
const deep_merge_1 = require("../utils/deep-merge");
exports.defaultFullstackServiceAWSArgs = {
storage: null,
connectionStringEnvironmentVariableName: "DATABASE_URL",
connectionStringSecret: null,
cluster: null,
vpc: null,
subnetIds: null,
securityGroups: null,
taskRole: null,
executionRole: null,
ecrRepository: null,
taskDefinitionArgs: null,
};
class AppFargate extends pulumi.ComponentResource {
constructor(name = 'App', args, opts) {
super("limgen:EcsClusterComponent", name, {}, opts);
this._args = Object.assign({}, exports.defaultFullstackServiceAWSArgs, args);
this.cluster = this.getCluster();
this.executionRole = this.getExecutionRole();
this.taskRole = this.getTaskRole();
this.ecrRepository = this.getEcrRepository();
this.image = this.getImage();
this.securityGroupIds = this.getSecurityGroups();
this.taskDefinitionArgs = this.getTaskDefinitionArgs();
this.service = this.getService();
this.registerOutputs({});
}
getTaskDefinitionArgs() {
return (0, deep_merge_1.deepMerge)({
taskRole: this.taskRole,
executionRole: this.executionRole,
container: {
name: this.getContainerName(),
image: this.image.imageUri,
cpu: 256,
memory: 512,
essential: true,
portMappings: [{
hostPort: 3000,
containerPort: 3000,
}],
},
}, this._args.taskDefinitionArgs);
}
getSecurityGroups() {
if (this._args.securityGroups) {
return this._args.securityGroups.map(sg => sg.id);
}
if (!this._args.loadBalancer) {
throw new Error("No security groups provided and no load balancer to derive them from");
}
if (!this._args.loadBalancer.defaultSecurityGroup) {
throw new Error("No security groups provided and load balancer has no default security group");
}
const appSecurityGroup = new aws.ec2.SecurityGroup("AppSecurityGroup", {
vpcId: this._args.vpc.vpcId,
ingress: [
{
protocol: "tcp",
fromPort: 3000,
toPort: 3000,
securityGroups: [this._args.loadBalancer.defaultSecurityGroup.apply(sg => sg.id)],
}
],
egress: [
{
protocol: "tcp",
fromPort: 0,
toPort: 65535,
cidrBlocks: ["0.0.0.0/0"],
}
],
});
return [appSecurityGroup.id];
}
getService() {
var _a;
const environmentParams = [];
if (this._args.cdnHostname) {
environmentParams.push({
name: "CDN_URL",
value: pulumi.interpolate `https://${this._args.cdnHostname}`,
});
}
if (this._args.storage) {
environmentParams.push({
name: "BUCKET_NAME",
value: this._args.storage.bucket,
});
}
const secrets = [];
if (this._args.connectionStringSecret) {
secrets.push({
name: "DATABASE_URL",
valueFrom: this._args.connectionStringSecret.arn,
});
}
const networkConfiguration = {
subnets: (_a = this._args.subnetIds) !== null && _a !== void 0 ? _a : this._args.vpc.publicSubnetIds,
assignPublicIp: true,
securityGroups: this.securityGroupIds
};
const loadBalancers = [];
if (this._args.loadBalancer) {
loadBalancers.push({
containerName: this.getContainerName(),
containerPort: 3000,
targetGroupArn: this._args.loadBalancer.defaultTargetGroup.arn,
});
}
const taskDefinitionArgs = {
taskRole: {
roleArn: this.taskRole.arn,
},
executionRole: {
roleArn: this.executionRole.arn,
},
container: {
name: this.getContainerName(),
image: this.image.imageUri,
cpu: 256,
memory: 512,
essential: true,
portMappings: [{
hostPort: 3000,
containerPort: 3000,
}],
environment: environmentParams,
secrets,
},
};
return new awsx.ecs.FargateService("Service", {
name: (0, prefixed_1.prefixed)('service'),
cluster: this.cluster.arn,
networkConfiguration,
loadBalancers,
taskDefinitionArgs
});
}
getImage() {
if (this._args.repositoryUrl) {
return new awsx.ecr.Image("Image", (0, deep_merge_1.deepMerge)({
repositoryUrl: this._args.repositoryUrl,
platform: "linux/amd64",
}, this._args.imageArgs));
}
if (!this.ecrRepository) {
throw new Error("No docker registry URL or repository provided");
}
const buildArgs = {};
if (this._args.cdnHostname) {
buildArgs.INPUT_CDN_URL = pulumi.interpolate `https://${this._args.cdnHostname}`;
}
return new awsx.ecr.Image("Image", (0, deep_merge_1.deepMerge)({
repositoryUrl: this.ecrRepository.url,
context: "../../..",
args: buildArgs,
platform: "linux/amd64",
}, this._args.imageArgs));
}
getEcrRepository() {
if (this._args.repositoryUrl) {
return null;
}
if (this._args.ecrRepository) {
return this._args.ecrRepository;
}
return new awsx.ecr.Repository("Repo", {
name: this.getContainerName(),
forceDelete: true,
});
}
getTaskRole() {
if (this._args.taskRole) {
return this._args.taskRole;
}
const taskRole = new aws.iam.Role("TaskRole", {
name: (0, prefixed_1.prefixed)('task-role'),
assumeRolePolicy: JSON.stringify({
Version: "2012-10-17",
Statement: [
{
Action: "sts:AssumeRole",
Effect: "Allow",
Principal: {
Service: "ecs-tasks.amazonaws.com",
},
},
],
}),
});
if (this._args.storage) {
new aws.iam.RolePolicy("TaskRolePolicy", {
name: (0, prefixed_1.prefixed)('task-role-policy'),
role: taskRole,
policy: {
Version: "2012-10-17",
Statement: [
{
Action: [
"s3:PutObject",
"s3:PutObjectAcl",
],
Effect: "Allow",
Resource: [pulumi.interpolate `${this._args.storage.arn}/*`],
}
],
}
});
}
return taskRole;
}
getCluster() {
if (this._args.cluster) {
return this._args.cluster;
}
return new aws.ecs.Cluster("Cluster", {
name: (0, prefixed_1.prefixed)('cluster'),
});
}
getExecutionRole() {
if (this._args.executionRole) {
return this._args.executionRole;
}
const executionRole = new aws.iam.Role("AppExecutionRole", {
name: (0, prefixed_1.prefixed)('execution-role'),
assumeRolePolicy: JSON.stringify({
Version: "2012-10-17",
Statement: [
{
Action: "sts:AssumeRole",
Effect: "Allow",
Principal: {
Service: "ecs-tasks.amazonaws.com",
},
},
],
}),
});
new aws.iam.PolicyAttachment("AppExecutionRoleAttachment", {
policyArn: "arn:aws:iam::aws:policy/service-role/AmazonECSTaskExecutionRolePolicy",
roles: [executionRole],
});
if (this._args.connectionStringSecret) {
new aws.iam.RolePolicy("ExecutionRolePolicy", {
role: executionRole,
name: (0, prefixed_1.prefixed)('execution-role-policy'),
policy: this._args.connectionStringSecret.arn.apply(arn => JSON.stringify({
Version: "2012-10-17",
Statement: [
{
Action: "secretsmanager:GetSecretValue",
Effect: "Allow",
Resource: [arn],
},
],
})),
});
}
return executionRole;
}
getContainerName() {
return (0, prefixed_1.prefixed)('app');
}
}
exports.AppFargate = AppFargate;