@studion/infra-code-blocks 0.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +11 -0
- package/dist/components/ecs.d.ts +33 -0
- package/dist/components/ecs.js +154 -0
- package/dist/components/project.d.ts +52 -0
- package/dist/components/project.js +191 -0
- package/dist/components/rds.d.ts +20 -0
- package/dist/components/rds.js +42 -0
- package/dist/components/redis.d.ts +14 -0
- package/dist/components/redis.js +18 -0
- package/dist/components/types.d.ts +7 -0
- package/dist/components/types.js +2 -0
- package/dist/constants.d.ts +22 -0
- package/dist/constants.js +27 -0
- package/dist/index.d.ts +4 -0
- package/dist/index.js +20 -0
- package/package.json +55 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
The MIT License (MIT)
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2023 Extension Engine, LLC
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import * as pulumi from '@pulumi/pulumi';
|
|
2
|
+
import * as aws from '@pulumi/aws';
|
|
3
|
+
import { Size } from './types';
|
|
4
|
+
export type RoleInlinePolicy = {
|
|
5
|
+
/**
|
|
6
|
+
* Name of the role policy.
|
|
7
|
+
*/
|
|
8
|
+
name?: pulumi.Input<string>;
|
|
9
|
+
/**
|
|
10
|
+
* Policy document as a JSON formatted string.
|
|
11
|
+
*/
|
|
12
|
+
policy?: pulumi.Input<string>;
|
|
13
|
+
};
|
|
14
|
+
export type EcsServiceArgs = {
|
|
15
|
+
image: pulumi.Input<string>;
|
|
16
|
+
port: pulumi.Input<number>;
|
|
17
|
+
cluster: aws.ecs.Cluster;
|
|
18
|
+
subnets: pulumi.Input<pulumi.Input<string>[]>;
|
|
19
|
+
securityGroupIds: pulumi.Input<pulumi.Input<string>[]>;
|
|
20
|
+
lb: aws.lb.LoadBalancer;
|
|
21
|
+
lbTargetGroup: aws.lb.TargetGroup;
|
|
22
|
+
lbListener: aws.lb.Listener;
|
|
23
|
+
desiredCount?: pulumi.Input<number>;
|
|
24
|
+
minCount?: pulumi.Input<number>;
|
|
25
|
+
maxCount?: pulumi.Input<number>;
|
|
26
|
+
size?: pulumi.Input<Size>;
|
|
27
|
+
environment?: aws.ecs.KeyValuePair[];
|
|
28
|
+
taskExecutionRoleInlinePolicies?: pulumi.Input<pulumi.Input<RoleInlinePolicy>[]>;
|
|
29
|
+
taskRoleInlinePolicies?: pulumi.Input<pulumi.Input<RoleInlinePolicy>[]>;
|
|
30
|
+
};
|
|
31
|
+
export declare class EcsService extends pulumi.ComponentResource {
|
|
32
|
+
constructor(name: string, args: EcsServiceArgs, opts?: pulumi.ComponentResourceOptions);
|
|
33
|
+
}
|
|
@@ -0,0 +1,154 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.EcsService = void 0;
|
|
4
|
+
const pulumi = require("@pulumi/pulumi");
|
|
5
|
+
const aws = require("@pulumi/aws");
|
|
6
|
+
const constants_1 = require("../constants");
|
|
7
|
+
const config = new pulumi.Config('aws');
|
|
8
|
+
const awsRegion = config.get('region');
|
|
9
|
+
const assumeRolePolicy = {
|
|
10
|
+
Version: '2012-10-17',
|
|
11
|
+
Statement: [
|
|
12
|
+
{
|
|
13
|
+
Action: 'sts:AssumeRole',
|
|
14
|
+
Principal: {
|
|
15
|
+
Service: 'ecs-tasks.amazonaws.com',
|
|
16
|
+
},
|
|
17
|
+
Effect: 'Allow',
|
|
18
|
+
Sid: '',
|
|
19
|
+
},
|
|
20
|
+
],
|
|
21
|
+
};
|
|
22
|
+
class EcsService extends pulumi.ComponentResource {
|
|
23
|
+
constructor(name, args, opts = {}) {
|
|
24
|
+
super('studion:ecs:Service', name, {}, opts);
|
|
25
|
+
const logGroup = new aws.cloudwatch.LogGroup(`${name}-log-group`, {
|
|
26
|
+
retentionInDays: 14,
|
|
27
|
+
name: `/ecs/${name}`,
|
|
28
|
+
}, { parent: this });
|
|
29
|
+
const taskExecutionRole = new aws.iam.Role(`${name}-ecs-task-exec-role`, {
|
|
30
|
+
name: `${name}-ecs-task-exec-role`,
|
|
31
|
+
assumeRolePolicy,
|
|
32
|
+
managedPolicyArns: [
|
|
33
|
+
'arn:aws:iam::aws:policy/CloudWatchFullAccess',
|
|
34
|
+
'arn:aws:iam::aws:policy/AmazonEC2ContainerRegistryFullAccess',
|
|
35
|
+
],
|
|
36
|
+
inlinePolicies: args.taskExecutionRoleInlinePolicies || [],
|
|
37
|
+
}, { parent: this });
|
|
38
|
+
const taskRole = new aws.iam.Role(`${name}-ecs-task-role`, {
|
|
39
|
+
name: `${name}-ecs-task-role`,
|
|
40
|
+
assumeRolePolicy,
|
|
41
|
+
inlinePolicies: args.taskRoleInlinePolicies || [],
|
|
42
|
+
}, { parent: this });
|
|
43
|
+
const parsedSize = pulumi.all([args.size || 'small']).apply(([size]) => {
|
|
44
|
+
const mapCapabilities = ({ cpu, memory }) => ({
|
|
45
|
+
cpu: String(cpu),
|
|
46
|
+
memory: String(memory),
|
|
47
|
+
});
|
|
48
|
+
if (typeof size === 'string') {
|
|
49
|
+
return mapCapabilities(constants_1.PredefinedSize[size]);
|
|
50
|
+
}
|
|
51
|
+
if (typeof size === 'object') {
|
|
52
|
+
return mapCapabilities(size);
|
|
53
|
+
}
|
|
54
|
+
throw Error('Incorrect EcsService size argument');
|
|
55
|
+
});
|
|
56
|
+
const taskDefinition = new aws.ecs.TaskDefinition(`${name}-task-definition`, {
|
|
57
|
+
family: `${name}-task-definition`,
|
|
58
|
+
networkMode: 'awsvpc',
|
|
59
|
+
executionRoleArn: taskExecutionRole.arn,
|
|
60
|
+
taskRoleArn: taskRole.arn,
|
|
61
|
+
cpu: parsedSize.cpu,
|
|
62
|
+
memory: parsedSize.memory,
|
|
63
|
+
requiresCompatibilities: ['FARGATE'],
|
|
64
|
+
containerDefinitions: pulumi
|
|
65
|
+
.all([
|
|
66
|
+
name,
|
|
67
|
+
args.image,
|
|
68
|
+
args.port,
|
|
69
|
+
args.environment || [],
|
|
70
|
+
logGroup.name,
|
|
71
|
+
awsRegion,
|
|
72
|
+
])
|
|
73
|
+
.apply(([containerName, image, port, environment, logGroup, region]) => {
|
|
74
|
+
return JSON.stringify([
|
|
75
|
+
{
|
|
76
|
+
name: containerName,
|
|
77
|
+
image,
|
|
78
|
+
essential: true,
|
|
79
|
+
portMappings: [
|
|
80
|
+
{
|
|
81
|
+
containerPort: port,
|
|
82
|
+
protocol: 'tcp',
|
|
83
|
+
},
|
|
84
|
+
],
|
|
85
|
+
logConfiguration: {
|
|
86
|
+
logDriver: 'awslogs',
|
|
87
|
+
options: {
|
|
88
|
+
'awslogs-group': logGroup,
|
|
89
|
+
'awslogs-region': region,
|
|
90
|
+
'awslogs-stream-prefix': 'ecs',
|
|
91
|
+
},
|
|
92
|
+
},
|
|
93
|
+
environment,
|
|
94
|
+
},
|
|
95
|
+
]);
|
|
96
|
+
}),
|
|
97
|
+
}, { parent: this });
|
|
98
|
+
const service = new aws.ecs.Service(`${name}-service`, {
|
|
99
|
+
name,
|
|
100
|
+
cluster: args.cluster.id,
|
|
101
|
+
launchType: 'FARGATE',
|
|
102
|
+
desiredCount: args.desiredCount || 1,
|
|
103
|
+
taskDefinition: taskDefinition.arn,
|
|
104
|
+
loadBalancers: [
|
|
105
|
+
{
|
|
106
|
+
containerName: name,
|
|
107
|
+
containerPort: args.port,
|
|
108
|
+
targetGroupArn: args.lbTargetGroup.arn,
|
|
109
|
+
},
|
|
110
|
+
],
|
|
111
|
+
networkConfiguration: {
|
|
112
|
+
assignPublicIp: true,
|
|
113
|
+
subnets: args.subnets,
|
|
114
|
+
securityGroups: args.securityGroupIds,
|
|
115
|
+
},
|
|
116
|
+
}, {
|
|
117
|
+
parent: this,
|
|
118
|
+
dependsOn: [args.lb, args.lbTargetGroup, args.lbListener],
|
|
119
|
+
});
|
|
120
|
+
const autoscalingTarget = new aws.appautoscaling.Target(`${name}-autoscale-target`, {
|
|
121
|
+
minCapacity: args.minCount || 1,
|
|
122
|
+
maxCapacity: args.maxCount || 10,
|
|
123
|
+
resourceId: pulumi.interpolate `service/${args.cluster.name}/${service.name}`,
|
|
124
|
+
serviceNamespace: 'ecs',
|
|
125
|
+
scalableDimension: 'ecs:service:DesiredCount',
|
|
126
|
+
}, { parent: this });
|
|
127
|
+
const memoryAutoscalingPolicy = new aws.appautoscaling.Policy(`${name}-memory-autoscale-policy`, {
|
|
128
|
+
policyType: 'TargetTrackingScaling',
|
|
129
|
+
resourceId: autoscalingTarget.resourceId,
|
|
130
|
+
scalableDimension: autoscalingTarget.scalableDimension,
|
|
131
|
+
serviceNamespace: autoscalingTarget.serviceNamespace,
|
|
132
|
+
targetTrackingScalingPolicyConfiguration: {
|
|
133
|
+
predefinedMetricSpecification: {
|
|
134
|
+
predefinedMetricType: 'ECSServiceAverageMemoryUtilization',
|
|
135
|
+
},
|
|
136
|
+
targetValue: 80,
|
|
137
|
+
},
|
|
138
|
+
}, { parent: this });
|
|
139
|
+
const cpuAutoscalingPolicy = new aws.appautoscaling.Policy(`${name}-cpu-autoscale-policy`, {
|
|
140
|
+
policyType: 'TargetTrackingScaling',
|
|
141
|
+
resourceId: autoscalingTarget.resourceId,
|
|
142
|
+
scalableDimension: autoscalingTarget.scalableDimension,
|
|
143
|
+
serviceNamespace: autoscalingTarget.serviceNamespace,
|
|
144
|
+
targetTrackingScalingPolicyConfiguration: {
|
|
145
|
+
predefinedMetricSpecification: {
|
|
146
|
+
predefinedMetricType: 'ECSServiceAverageCPUUtilization',
|
|
147
|
+
},
|
|
148
|
+
targetValue: 60,
|
|
149
|
+
},
|
|
150
|
+
}, { parent: this });
|
|
151
|
+
this.registerOutputs();
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
exports.EcsService = EcsService;
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
import * as pulumi from '@pulumi/pulumi';
|
|
2
|
+
import * as aws from '@pulumi/aws';
|
|
3
|
+
import * as awsx from '@pulumi/awsx';
|
|
4
|
+
import * as upstash from '@upstash/pulumi';
|
|
5
|
+
import { Rds, RdsArgs } from './rds';
|
|
6
|
+
import { EcsService, EcsServiceArgs } from './ecs';
|
|
7
|
+
import { Redis, RedisArgs } from './redis';
|
|
8
|
+
import { Environment } from '../constants';
|
|
9
|
+
export type Service = Rds | Redis | EcsService;
|
|
10
|
+
export type Services = Record<string, Service>;
|
|
11
|
+
type ServiceArgs = {
|
|
12
|
+
serviceName: string;
|
|
13
|
+
};
|
|
14
|
+
export type DatabaseService = {
|
|
15
|
+
type: 'DATABASE';
|
|
16
|
+
} & ServiceArgs & Pick<RdsArgs, 'dbName' | 'username' | 'password' | 'allocatedStorage' | 'maxAllocatedStorage' | 'instanceClass' | 'applyImmediately'>;
|
|
17
|
+
export type RedisService = {
|
|
18
|
+
type: 'REDIS';
|
|
19
|
+
} & ServiceArgs & Pick<RedisArgs, 'dbName' | 'region'>;
|
|
20
|
+
export type WebServerService = {
|
|
21
|
+
type: 'WEB_SERVER';
|
|
22
|
+
environment?: aws.ecs.KeyValuePair[] | ((services: Services) => aws.ecs.KeyValuePair[]);
|
|
23
|
+
healtCheckPath?: pulumi.Input<string>;
|
|
24
|
+
} & ServiceArgs & Pick<EcsServiceArgs, 'image' | 'port' | 'desiredCount' | 'minCount' | 'maxCount' | 'size'>;
|
|
25
|
+
export type Environment = (typeof Environment)[keyof typeof Environment];
|
|
26
|
+
export type ProjectArgs = {
|
|
27
|
+
services: (DatabaseService | RedisService | WebServerService)[];
|
|
28
|
+
environment: Environment;
|
|
29
|
+
};
|
|
30
|
+
export declare class Project extends pulumi.ComponentResource {
|
|
31
|
+
name: string;
|
|
32
|
+
environment: Environment;
|
|
33
|
+
vpc: awsx.ec2.Vpc;
|
|
34
|
+
dbSubnetGroup: aws.rds.SubnetGroup | null;
|
|
35
|
+
dbSecurityGroup: aws.ec2.SecurityGroup | null;
|
|
36
|
+
cluster: aws.ecs.Cluster | null;
|
|
37
|
+
lbSecurityGroup: aws.ec2.SecurityGroup | null;
|
|
38
|
+
lb: aws.lb.LoadBalancer | null;
|
|
39
|
+
ecsServiceSecurityGroup: aws.ec2.SecurityGroup | null;
|
|
40
|
+
upstashProvider: upstash.Provider | null;
|
|
41
|
+
services: Services;
|
|
42
|
+
constructor(name: string, args: ProjectArgs, opts?: pulumi.ComponentResourceOptions);
|
|
43
|
+
private createVpc;
|
|
44
|
+
private createServices;
|
|
45
|
+
private createDatabasePrerequisites;
|
|
46
|
+
private createRedisPrerequisites;
|
|
47
|
+
private createWebServerPrerequisites;
|
|
48
|
+
private createDatabaseService;
|
|
49
|
+
private createRedisService;
|
|
50
|
+
private createWebServerService;
|
|
51
|
+
}
|
|
52
|
+
export {};
|
|
@@ -0,0 +1,191 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __rest = (this && this.__rest) || function (s, e) {
|
|
3
|
+
var t = {};
|
|
4
|
+
for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p) && e.indexOf(p) < 0)
|
|
5
|
+
t[p] = s[p];
|
|
6
|
+
if (s != null && typeof Object.getOwnPropertySymbols === "function")
|
|
7
|
+
for (var i = 0, p = Object.getOwnPropertySymbols(s); i < p.length; i++) {
|
|
8
|
+
if (e.indexOf(p[i]) < 0 && Object.prototype.propertyIsEnumerable.call(s, p[i]))
|
|
9
|
+
t[p[i]] = s[p[i]];
|
|
10
|
+
}
|
|
11
|
+
return t;
|
|
12
|
+
};
|
|
13
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
14
|
+
exports.Project = void 0;
|
|
15
|
+
const pulumi = require("@pulumi/pulumi");
|
|
16
|
+
const aws = require("@pulumi/aws");
|
|
17
|
+
const awsx = require("@pulumi/awsx");
|
|
18
|
+
const upstash = require("@upstash/pulumi");
|
|
19
|
+
const rds_1 = require("./rds");
|
|
20
|
+
const ecs_1 = require("./ecs");
|
|
21
|
+
const redis_1 = require("./redis");
|
|
22
|
+
class Project extends pulumi.ComponentResource {
|
|
23
|
+
constructor(name, args, opts = {}) {
|
|
24
|
+
super('studion:Project', name, {}, opts);
|
|
25
|
+
this.dbSubnetGroup = null;
|
|
26
|
+
this.dbSecurityGroup = null;
|
|
27
|
+
this.cluster = null;
|
|
28
|
+
this.lbSecurityGroup = null;
|
|
29
|
+
this.lb = null;
|
|
30
|
+
this.ecsServiceSecurityGroup = null;
|
|
31
|
+
this.upstashProvider = null;
|
|
32
|
+
this.services = {};
|
|
33
|
+
const { services, environment } = args;
|
|
34
|
+
this.name = name;
|
|
35
|
+
this.environment = environment;
|
|
36
|
+
this.vpc = this.createVpc();
|
|
37
|
+
this.createServices(services);
|
|
38
|
+
this.registerOutputs();
|
|
39
|
+
}
|
|
40
|
+
createVpc() {
|
|
41
|
+
const vpc = new awsx.ec2.Vpc(`${this.name}-vpc`, {
|
|
42
|
+
numberOfAvailabilityZones: 2,
|
|
43
|
+
enableDnsHostnames: true,
|
|
44
|
+
enableDnsSupport: true,
|
|
45
|
+
}, { parent: this });
|
|
46
|
+
return vpc;
|
|
47
|
+
}
|
|
48
|
+
createServices(services) {
|
|
49
|
+
const hasDatabaseService = services.some(it => it.type === 'DATABASE');
|
|
50
|
+
const hasRedisService = services.some(it => it.type === 'REDIS');
|
|
51
|
+
const hasWebServerService = services.some(it => it.type === 'WEB_SERVER');
|
|
52
|
+
if (hasDatabaseService)
|
|
53
|
+
this.createDatabasePrerequisites();
|
|
54
|
+
if (hasRedisService)
|
|
55
|
+
this.createRedisPrerequisites();
|
|
56
|
+
if (hasWebServerService)
|
|
57
|
+
this.createWebServerPrerequisites();
|
|
58
|
+
services.forEach(it => {
|
|
59
|
+
if (it.type === 'DATABASE')
|
|
60
|
+
this.createDatabaseService(it);
|
|
61
|
+
if (it.type === 'REDIS')
|
|
62
|
+
this.createRedisService(it);
|
|
63
|
+
if (it.type === 'WEB_SERVER')
|
|
64
|
+
this.createWebServerService(it);
|
|
65
|
+
});
|
|
66
|
+
}
|
|
67
|
+
createDatabasePrerequisites() {
|
|
68
|
+
this.dbSubnetGroup = new aws.rds.SubnetGroup('db-subnet-group', {
|
|
69
|
+
subnetIds: this.vpc.privateSubnetIds,
|
|
70
|
+
}, { parent: this });
|
|
71
|
+
this.dbSecurityGroup = new aws.ec2.SecurityGroup('db-security-group', {
|
|
72
|
+
vpcId: this.vpc.vpcId,
|
|
73
|
+
ingress: [
|
|
74
|
+
{
|
|
75
|
+
protocol: 'tcp',
|
|
76
|
+
fromPort: 5432,
|
|
77
|
+
toPort: 5432,
|
|
78
|
+
cidrBlocks: [this.vpc.vpc.cidrBlock],
|
|
79
|
+
},
|
|
80
|
+
],
|
|
81
|
+
}, { parent: this });
|
|
82
|
+
}
|
|
83
|
+
createRedisPrerequisites() {
|
|
84
|
+
const upstashConfig = new pulumi.Config('upstash');
|
|
85
|
+
this.upstashProvider = new upstash.Provider('upstash', {
|
|
86
|
+
email: upstashConfig.requireSecret('email'),
|
|
87
|
+
apiKey: upstashConfig.requireSecret('apiKey'),
|
|
88
|
+
});
|
|
89
|
+
}
|
|
90
|
+
createWebServerPrerequisites() {
|
|
91
|
+
this.cluster = new aws.ecs.Cluster(`${this.name}-cluster`, { name: this.name }, { parent: this });
|
|
92
|
+
this.lbSecurityGroup = new aws.ec2.SecurityGroup('ecs-lb-security-group', {
|
|
93
|
+
vpcId: this.vpc.vpcId,
|
|
94
|
+
ingress: [
|
|
95
|
+
{
|
|
96
|
+
fromPort: 80,
|
|
97
|
+
toPort: 80,
|
|
98
|
+
protocol: 'tcp',
|
|
99
|
+
cidrBlocks: ['0.0.0.0/0'],
|
|
100
|
+
},
|
|
101
|
+
],
|
|
102
|
+
egress: [
|
|
103
|
+
{
|
|
104
|
+
fromPort: 0,
|
|
105
|
+
toPort: 0,
|
|
106
|
+
protocol: '-1',
|
|
107
|
+
cidrBlocks: ['0.0.0.0/0'],
|
|
108
|
+
},
|
|
109
|
+
],
|
|
110
|
+
}, { parent: this });
|
|
111
|
+
this.lb = new aws.lb.LoadBalancer('ecs-load-balancer', {
|
|
112
|
+
loadBalancerType: 'application',
|
|
113
|
+
subnets: this.vpc.publicSubnetIds,
|
|
114
|
+
securityGroups: [this.lbSecurityGroup.id],
|
|
115
|
+
internal: false,
|
|
116
|
+
ipAddressType: 'ipv4',
|
|
117
|
+
}, { parent: this });
|
|
118
|
+
this.ecsServiceSecurityGroup = new aws.ec2.SecurityGroup('ecs-service-security-group', {
|
|
119
|
+
vpcId: this.vpc.vpcId,
|
|
120
|
+
ingress: [
|
|
121
|
+
{
|
|
122
|
+
fromPort: 0,
|
|
123
|
+
toPort: 0,
|
|
124
|
+
protocol: '-1',
|
|
125
|
+
securityGroups: [this.lbSecurityGroup.id],
|
|
126
|
+
},
|
|
127
|
+
],
|
|
128
|
+
egress: [
|
|
129
|
+
{
|
|
130
|
+
fromPort: 0,
|
|
131
|
+
toPort: 0,
|
|
132
|
+
protocol: '-1',
|
|
133
|
+
cidrBlocks: ['0.0.0.0/0'],
|
|
134
|
+
},
|
|
135
|
+
],
|
|
136
|
+
}, { parent: this });
|
|
137
|
+
}
|
|
138
|
+
createDatabaseService(options) {
|
|
139
|
+
if (!this.dbSecurityGroup || !this.dbSubnetGroup)
|
|
140
|
+
return;
|
|
141
|
+
const { serviceName, type } = options, rdsOptions = __rest(options, ["serviceName", "type"]);
|
|
142
|
+
const instance = new rds_1.Rds(serviceName, Object.assign(Object.assign({}, rdsOptions), { subnetGroupName: this.dbSubnetGroup.name, securityGroupIds: [this.dbSecurityGroup.id] }), { parent: this });
|
|
143
|
+
this.services[serviceName] = instance;
|
|
144
|
+
}
|
|
145
|
+
createRedisService(options) {
|
|
146
|
+
if (!this.upstashProvider)
|
|
147
|
+
return;
|
|
148
|
+
const { serviceName } = options, redisOptions = __rest(options, ["serviceName"]);
|
|
149
|
+
const instance = new redis_1.Redis(serviceName, redisOptions, {
|
|
150
|
+
parent: this,
|
|
151
|
+
provider: this.upstashProvider,
|
|
152
|
+
});
|
|
153
|
+
this.services[options.serviceName] = instance;
|
|
154
|
+
}
|
|
155
|
+
createWebServerService(options) {
|
|
156
|
+
if (!this.cluster || !this.ecsServiceSecurityGroup || !this.lb) {
|
|
157
|
+
return;
|
|
158
|
+
}
|
|
159
|
+
const { serviceName, environment, healtCheckPath = '/healtcheck' } = options, ecsOptions = __rest(options, ["serviceName", "environment", "healtCheckPath"]);
|
|
160
|
+
const lbTargetGroup = new aws.lb.TargetGroup(`${serviceName}-tg`, {
|
|
161
|
+
port: ecsOptions.port,
|
|
162
|
+
protocol: 'HTTP',
|
|
163
|
+
targetType: 'ip',
|
|
164
|
+
vpcId: this.vpc.vpcId,
|
|
165
|
+
healthCheck: {
|
|
166
|
+
healthyThreshold: 3,
|
|
167
|
+
unhealthyThreshold: 2,
|
|
168
|
+
interval: 60,
|
|
169
|
+
timeout: 5,
|
|
170
|
+
path: healtCheckPath,
|
|
171
|
+
},
|
|
172
|
+
}, { parent: this, dependsOn: [this.lb] });
|
|
173
|
+
const lbListener = new aws.lb.Listener(`${serviceName}-lb-listener`, {
|
|
174
|
+
loadBalancerArn: this.lb.arn,
|
|
175
|
+
port: 80,
|
|
176
|
+
defaultActions: [
|
|
177
|
+
{
|
|
178
|
+
type: 'forward',
|
|
179
|
+
targetGroupArn: lbTargetGroup.arn,
|
|
180
|
+
},
|
|
181
|
+
],
|
|
182
|
+
}, { parent: this, dependsOn: [this.lb, lbTargetGroup] });
|
|
183
|
+
const parsedEnv = typeof environment === 'function'
|
|
184
|
+
? environment(this.services)
|
|
185
|
+
: environment;
|
|
186
|
+
const instance = new ecs_1.EcsService(serviceName, Object.assign(Object.assign({}, ecsOptions), { cluster: this.cluster, subnets: this.vpc.publicSubnetIds, securityGroupIds: [this.ecsServiceSecurityGroup.id], lb: this.lb, lbTargetGroup,
|
|
187
|
+
lbListener, environment: parsedEnv }), { parent: this });
|
|
188
|
+
this.services[options.serviceName] = instance;
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
exports.Project = Project;
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import * as aws from '@pulumi/aws';
|
|
2
|
+
import * as pulumi from '@pulumi/pulumi';
|
|
3
|
+
export type RdsArgs = {
|
|
4
|
+
dbName: pulumi.Input<string>;
|
|
5
|
+
username: pulumi.Input<string>;
|
|
6
|
+
password: pulumi.Input<string>;
|
|
7
|
+
subnetGroupName: pulumi.Input<string>;
|
|
8
|
+
securityGroupIds: pulumi.Input<pulumi.Input<string>[]>;
|
|
9
|
+
publiclyAccessible?: pulumi.Input<boolean>;
|
|
10
|
+
applyImmediately?: pulumi.Input<boolean>;
|
|
11
|
+
skipFinalSnapshot?: pulumi.Input<boolean>;
|
|
12
|
+
allocatedStorage?: pulumi.Input<number>;
|
|
13
|
+
maxAllocatedStorage?: pulumi.Input<number>;
|
|
14
|
+
instanceClass?: pulumi.Input<string>;
|
|
15
|
+
};
|
|
16
|
+
export type RdsInstance = aws.rds.Instance;
|
|
17
|
+
export declare class Rds extends pulumi.ComponentResource {
|
|
18
|
+
instance: RdsInstance;
|
|
19
|
+
constructor(name: string, args: RdsArgs, opts?: pulumi.ComponentResourceOptions);
|
|
20
|
+
}
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.Rds = void 0;
|
|
4
|
+
const aws = require("@pulumi/aws");
|
|
5
|
+
const pulumi = require("@pulumi/pulumi");
|
|
6
|
+
class Rds extends pulumi.ComponentResource {
|
|
7
|
+
constructor(name, args, opts = {}) {
|
|
8
|
+
super('studion:rds:Instance', name, {}, opts);
|
|
9
|
+
const kms = new aws.kms.Key(`${name}-rds-key`, {
|
|
10
|
+
customerMasterKeySpec: 'SYMMETRIC_DEFAULT',
|
|
11
|
+
isEnabled: true,
|
|
12
|
+
keyUsage: 'ENCRYPT_DECRYPT',
|
|
13
|
+
multiRegion: false,
|
|
14
|
+
enableKeyRotation: true,
|
|
15
|
+
}, { parent: this });
|
|
16
|
+
this.instance = new aws.rds.Instance(`${name}-rds`, {
|
|
17
|
+
identifier: name,
|
|
18
|
+
engine: 'postgres',
|
|
19
|
+
engineVersion: '14.9',
|
|
20
|
+
allocatedStorage: args.allocatedStorage || 20,
|
|
21
|
+
maxAllocatedStorage: args.maxAllocatedStorage || 100,
|
|
22
|
+
instanceClass: args.instanceClass || 'db.t3.micro',
|
|
23
|
+
dbName: args.dbName,
|
|
24
|
+
username: args.username,
|
|
25
|
+
password: args.password,
|
|
26
|
+
dbSubnetGroupName: args.subnetGroupName,
|
|
27
|
+
vpcSecurityGroupIds: args.securityGroupIds,
|
|
28
|
+
storageEncrypted: true,
|
|
29
|
+
kmsKeyId: kms.arn,
|
|
30
|
+
publiclyAccessible: args.publiclyAccessible || false,
|
|
31
|
+
skipFinalSnapshot: args.skipFinalSnapshot || false,
|
|
32
|
+
applyImmediately: args.applyImmediately || false,
|
|
33
|
+
autoMinorVersionUpgrade: true,
|
|
34
|
+
maintenanceWindow: 'Mon:07:00-Mon:07:30',
|
|
35
|
+
finalSnapshotIdentifier: `${name}-final-snapshot`,
|
|
36
|
+
backupWindow: '06:00-06:30',
|
|
37
|
+
backupRetentionPeriod: 14,
|
|
38
|
+
}, { parent: this });
|
|
39
|
+
this.registerOutputs();
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
exports.Rds = Rds;
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import * as pulumi from '@pulumi/pulumi';
|
|
2
|
+
import * as upstash from '@upstash/pulumi';
|
|
3
|
+
export type RedisArgs = {
|
|
4
|
+
dbName: pulumi.Input<string>;
|
|
5
|
+
region?: pulumi.Input<string>;
|
|
6
|
+
};
|
|
7
|
+
export interface RedisOptions extends pulumi.ComponentResourceOptions {
|
|
8
|
+
provider: upstash.Provider;
|
|
9
|
+
}
|
|
10
|
+
export type RedisInstance = upstash.RedisDatabase;
|
|
11
|
+
export declare class Redis extends pulumi.ComponentResource {
|
|
12
|
+
instance: RedisInstance;
|
|
13
|
+
constructor(name: string, args: RedisArgs, opts: RedisOptions);
|
|
14
|
+
}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.Redis = void 0;
|
|
4
|
+
const pulumi = require("@pulumi/pulumi");
|
|
5
|
+
const upstash = require("@upstash/pulumi");
|
|
6
|
+
class Redis extends pulumi.ComponentResource {
|
|
7
|
+
constructor(name, args, opts) {
|
|
8
|
+
super('studion:redis:Instance', name, {}, opts);
|
|
9
|
+
this.instance = new upstash.RedisDatabase(name, {
|
|
10
|
+
databaseName: args.dbName,
|
|
11
|
+
region: args.region || 'us-east-1',
|
|
12
|
+
eviction: true,
|
|
13
|
+
tls: true,
|
|
14
|
+
}, { provider: opts.provider, parent: this });
|
|
15
|
+
this.registerOutputs();
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
exports.Redis = Redis;
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
export declare const PredefinedSize: {
|
|
2
|
+
readonly small: {
|
|
3
|
+
readonly cpu: number;
|
|
4
|
+
readonly memory: number;
|
|
5
|
+
};
|
|
6
|
+
readonly medium: {
|
|
7
|
+
readonly cpu: number;
|
|
8
|
+
readonly memory: 1024;
|
|
9
|
+
};
|
|
10
|
+
readonly large: {
|
|
11
|
+
readonly cpu: 1024;
|
|
12
|
+
readonly memory: number;
|
|
13
|
+
};
|
|
14
|
+
readonly xlarge: {
|
|
15
|
+
readonly cpu: number;
|
|
16
|
+
readonly memory: number;
|
|
17
|
+
};
|
|
18
|
+
};
|
|
19
|
+
export declare const Environment: {
|
|
20
|
+
readonly DEVELOPMENT: "DEVELOPMENT";
|
|
21
|
+
readonly PRODUCTION: "PRODUCTION";
|
|
22
|
+
};
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.Environment = exports.PredefinedSize = void 0;
|
|
4
|
+
const CPU_1_VCPU = 1024;
|
|
5
|
+
const MEMORY_1GB = 1024;
|
|
6
|
+
exports.PredefinedSize = {
|
|
7
|
+
small: {
|
|
8
|
+
cpu: CPU_1_VCPU / 4,
|
|
9
|
+
memory: MEMORY_1GB / 2, // 0.5 GB memory
|
|
10
|
+
},
|
|
11
|
+
medium: {
|
|
12
|
+
cpu: CPU_1_VCPU / 2,
|
|
13
|
+
memory: MEMORY_1GB, // 1 GB memory
|
|
14
|
+
},
|
|
15
|
+
large: {
|
|
16
|
+
cpu: CPU_1_VCPU,
|
|
17
|
+
memory: MEMORY_1GB * 2, // 2 GB memory
|
|
18
|
+
},
|
|
19
|
+
xlarge: {
|
|
20
|
+
cpu: CPU_1_VCPU * 2,
|
|
21
|
+
memory: MEMORY_1GB * 4, // 4 GB memory
|
|
22
|
+
},
|
|
23
|
+
};
|
|
24
|
+
exports.Environment = {
|
|
25
|
+
DEVELOPMENT: 'DEVELOPMENT',
|
|
26
|
+
PRODUCTION: 'PRODUCTION',
|
|
27
|
+
};
|
package/dist/index.d.ts
ADDED
package/dist/index.js
ADDED
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __exportStar = (this && this.__exportStar) || function(m, exports) {
|
|
14
|
+
for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
|
|
15
|
+
};
|
|
16
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
17
|
+
__exportStar(require("./components/ecs"), exports);
|
|
18
|
+
__exportStar(require("./components/rds"), exports);
|
|
19
|
+
__exportStar(require("./components/redis"), exports);
|
|
20
|
+
__exportStar(require("./components/project"), exports);
|
package/package.json
ADDED
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@studion/infra-code-blocks",
|
|
3
|
+
"version": "0.0.1",
|
|
4
|
+
"description": "Studion common infra components",
|
|
5
|
+
"keywords": [
|
|
6
|
+
"infrastructure",
|
|
7
|
+
"Pulumi",
|
|
8
|
+
"components",
|
|
9
|
+
"Studion"
|
|
10
|
+
],
|
|
11
|
+
"homepage": "https://github.com/ExtensionEngine/infra-code-blocks#readme",
|
|
12
|
+
"bugs": {
|
|
13
|
+
"url": "https://github.com/ExtensionEngine/infra-code-blocks/issues"
|
|
14
|
+
},
|
|
15
|
+
"repository": "git+https://github.com/ExtensionEngine/infra-code-blocks.git",
|
|
16
|
+
"license": "MIT",
|
|
17
|
+
"author": {
|
|
18
|
+
"name": "Studion",
|
|
19
|
+
"email": "info@gostudion.com",
|
|
20
|
+
"url": "https://www.gostudion.com"
|
|
21
|
+
},
|
|
22
|
+
"main": "dist/index.js",
|
|
23
|
+
"files": [
|
|
24
|
+
"dist"
|
|
25
|
+
],
|
|
26
|
+
"scripts": {
|
|
27
|
+
"build": "npm run clean && tsc",
|
|
28
|
+
"clean": "rm -rf dist",
|
|
29
|
+
"format": "prettier -w .",
|
|
30
|
+
"release": "release-it",
|
|
31
|
+
"test": ""
|
|
32
|
+
},
|
|
33
|
+
"prettier": "@studion/prettier-config",
|
|
34
|
+
"dependencies": {
|
|
35
|
+
"@pulumi/aws": "^5.0.0",
|
|
36
|
+
"@pulumi/awsx": "^1.0.0",
|
|
37
|
+
"@pulumi/pulumi": "^3.0.0",
|
|
38
|
+
"@upstash/pulumi": "^0.2.0"
|
|
39
|
+
},
|
|
40
|
+
"devDependencies": {
|
|
41
|
+
"@studion/prettier-config": "^0.1.0",
|
|
42
|
+
"@types/node": "^18",
|
|
43
|
+
"prettier": "^3.0.3",
|
|
44
|
+
"release-it": "^16.2.1",
|
|
45
|
+
"typescript": "^5.2.2"
|
|
46
|
+
},
|
|
47
|
+
"publishConfig": {
|
|
48
|
+
"access": "public"
|
|
49
|
+
},
|
|
50
|
+
"release-it": {
|
|
51
|
+
"github": {
|
|
52
|
+
"release": true
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
}
|