@studion/infra-code-blocks 0.1.9 → 0.2.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/README.md +272 -28
- package/dist/components/database.d.ts +12 -8
- package/dist/components/database.js +8 -8
- package/dist/components/ec2-ssm-connect.d.ts +6 -2
- package/dist/components/ec2-ssm-connect.js +7 -7
- package/dist/components/ecs-service.d.ts +125 -0
- package/dist/components/ecs-service.js +323 -0
- package/dist/components/mongo.d.ts +27 -0
- package/dist/components/mongo.js +63 -0
- package/dist/components/nuxt-ssr.d.ts +43 -0
- package/dist/components/nuxt-ssr.js +277 -0
- package/dist/components/project.d.ts +32 -15
- package/dist/components/project.js +62 -20
- package/dist/components/web-server.d.ts +12 -81
- package/dist/components/web-server.js +60 -254
- package/dist/index.d.ts +3 -0
- package/dist/index.js +3 -0
- package/package.json +1 -1
|
@@ -0,0 +1,277 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.NuxtSSR = void 0;
|
|
4
|
+
const pulumi = require("@pulumi/pulumi");
|
|
5
|
+
const aws = require("@pulumi/aws");
|
|
6
|
+
const random = require("@pulumi/random");
|
|
7
|
+
const constants_1 = require("../constants");
|
|
8
|
+
const acm_certificate_1 = require("./acm-certificate");
|
|
9
|
+
const ecs_service_1 = require("./ecs-service");
|
|
10
|
+
const defaults = {
|
|
11
|
+
healthCheckPath: '/',
|
|
12
|
+
};
|
|
13
|
+
class NuxtSSR extends pulumi.ComponentResource {
|
|
14
|
+
constructor(name, args, opts = {}) {
|
|
15
|
+
super('studion:NuxtSSR', name, args, opts);
|
|
16
|
+
const { vpcId, domain, hostedZoneId, tags } = args;
|
|
17
|
+
const hasCustomDomain = domain && hostedZoneId;
|
|
18
|
+
if (domain && !hostedZoneId) {
|
|
19
|
+
throw new Error('NuxtSSR:hostedZoneId must be provided when the domain is specified');
|
|
20
|
+
}
|
|
21
|
+
this.name = name;
|
|
22
|
+
if (hasCustomDomain) {
|
|
23
|
+
this.certificate = this.createTlsCertificate({ domain, hostedZoneId });
|
|
24
|
+
}
|
|
25
|
+
this.customCFHeader = this.createCustomCFHeader();
|
|
26
|
+
const { lb, lbTargetGroup, lbHttpListener, lbSecurityGroup } = this.createLoadBalancer(args);
|
|
27
|
+
this.lb = lb;
|
|
28
|
+
this.lbTargetGroup = lbTargetGroup;
|
|
29
|
+
this.lbHttpListener = lbHttpListener;
|
|
30
|
+
this.lbSecurityGroup = lbSecurityGroup;
|
|
31
|
+
this.serviceSecurityGroup = this.createSecurityGroup(vpcId);
|
|
32
|
+
this.service = this.createEcsService(args);
|
|
33
|
+
this.cloudfront = this.createCloudfrontDistribution({ domain, tags });
|
|
34
|
+
if (hasCustomDomain) {
|
|
35
|
+
this.createDnsRecord({ domain, hostedZoneId });
|
|
36
|
+
}
|
|
37
|
+
this.registerOutputs();
|
|
38
|
+
}
|
|
39
|
+
createTlsCertificate({ domain, hostedZoneId, }) {
|
|
40
|
+
const certificate = new acm_certificate_1.AcmCertificate(`${domain}-acm-certificate`, {
|
|
41
|
+
domain,
|
|
42
|
+
hostedZoneId,
|
|
43
|
+
}, { parent: this });
|
|
44
|
+
return certificate;
|
|
45
|
+
}
|
|
46
|
+
createCustomCFHeader() {
|
|
47
|
+
const headerNameOpts = {
|
|
48
|
+
length: 4,
|
|
49
|
+
special: false,
|
|
50
|
+
numeric: false,
|
|
51
|
+
lower: false,
|
|
52
|
+
upper: true,
|
|
53
|
+
};
|
|
54
|
+
const headerNameSegment1 = new random.RandomString(`${this.name}-cf-header-name-segment1`, headerNameOpts, { parent: this });
|
|
55
|
+
const headerNameSegment2 = new random.RandomString(`${this.name}-cf-header-name-segment2`, headerNameOpts, { parent: this });
|
|
56
|
+
const headerValue = new random.RandomString(`${this.name}-cf-header-value`, {
|
|
57
|
+
length: 36,
|
|
58
|
+
special: false,
|
|
59
|
+
numeric: true,
|
|
60
|
+
lower: true,
|
|
61
|
+
upper: true,
|
|
62
|
+
}, { parent: this });
|
|
63
|
+
const headerName = pulumi
|
|
64
|
+
.all([headerNameSegment1.result, headerNameSegment2.result])
|
|
65
|
+
.apply(([segment1, segment2]) => {
|
|
66
|
+
return `X-${segment1}-${segment2}`;
|
|
67
|
+
});
|
|
68
|
+
return { name: headerName, value: headerValue.result };
|
|
69
|
+
}
|
|
70
|
+
createLoadBalancer({ vpcId, publicSubnetIds, port, healthCheckPath, }) {
|
|
71
|
+
const lbSecurityGroup = new aws.ec2.SecurityGroup(`${this.name}-lb-security-group`, {
|
|
72
|
+
vpcId,
|
|
73
|
+
ingress: [
|
|
74
|
+
{
|
|
75
|
+
protocol: 'tcp',
|
|
76
|
+
fromPort: 80,
|
|
77
|
+
toPort: 80,
|
|
78
|
+
cidrBlocks: ['0.0.0.0/0'],
|
|
79
|
+
},
|
|
80
|
+
],
|
|
81
|
+
egress: [
|
|
82
|
+
{
|
|
83
|
+
fromPort: 0,
|
|
84
|
+
toPort: 0,
|
|
85
|
+
protocol: '-1',
|
|
86
|
+
cidrBlocks: ['0.0.0.0/0'],
|
|
87
|
+
},
|
|
88
|
+
],
|
|
89
|
+
tags: constants_1.commonTags,
|
|
90
|
+
}, { parent: this });
|
|
91
|
+
const lb = new aws.lb.LoadBalancer(`${this.name}-lb`, {
|
|
92
|
+
namePrefix: 'lb-',
|
|
93
|
+
loadBalancerType: 'application',
|
|
94
|
+
subnets: publicSubnetIds,
|
|
95
|
+
securityGroups: [lbSecurityGroup.id],
|
|
96
|
+
internal: false,
|
|
97
|
+
ipAddressType: 'ipv4',
|
|
98
|
+
tags: Object.assign(Object.assign({}, constants_1.commonTags), { Name: `${this.name}-lb` }),
|
|
99
|
+
}, { parent: this });
|
|
100
|
+
const lbTargetGroup = new aws.lb.TargetGroup(`${this.name}-lb-tg`, {
|
|
101
|
+
namePrefix: 'lb-tg-',
|
|
102
|
+
port,
|
|
103
|
+
protocol: 'HTTP',
|
|
104
|
+
targetType: 'ip',
|
|
105
|
+
vpcId,
|
|
106
|
+
healthCheck: {
|
|
107
|
+
healthyThreshold: 3,
|
|
108
|
+
unhealthyThreshold: 2,
|
|
109
|
+
interval: 60,
|
|
110
|
+
timeout: 5,
|
|
111
|
+
path: healthCheckPath || defaults.healthCheckPath,
|
|
112
|
+
},
|
|
113
|
+
tags: Object.assign(Object.assign({}, constants_1.commonTags), { Name: `${this.name}-lb-target-group` }),
|
|
114
|
+
}, { parent: this, dependsOn: [this.lb] });
|
|
115
|
+
const lbHttpListener = new aws.lb.Listener(`${this.name}-lb-listener-80`, {
|
|
116
|
+
loadBalancerArn: lb.arn,
|
|
117
|
+
port: 80,
|
|
118
|
+
defaultActions: [
|
|
119
|
+
{
|
|
120
|
+
type: 'fixed-response',
|
|
121
|
+
fixedResponse: {
|
|
122
|
+
statusCode: '403',
|
|
123
|
+
messageBody: 'Not Allowed',
|
|
124
|
+
contentType: 'text/plain',
|
|
125
|
+
},
|
|
126
|
+
},
|
|
127
|
+
],
|
|
128
|
+
tags: constants_1.commonTags,
|
|
129
|
+
}, { parent: this });
|
|
130
|
+
const lbHttpListenerRule = new aws.lb.ListenerRule(`${this.name}-lb-listener-rule`, {
|
|
131
|
+
listenerArn: lbHttpListener.arn,
|
|
132
|
+
priority: 1,
|
|
133
|
+
actions: [
|
|
134
|
+
{
|
|
135
|
+
type: 'forward',
|
|
136
|
+
targetGroupArn: lbTargetGroup.arn,
|
|
137
|
+
},
|
|
138
|
+
],
|
|
139
|
+
conditions: [
|
|
140
|
+
{
|
|
141
|
+
httpHeader: {
|
|
142
|
+
httpHeaderName: this.customCFHeader.name,
|
|
143
|
+
values: [this.customCFHeader.value],
|
|
144
|
+
},
|
|
145
|
+
},
|
|
146
|
+
],
|
|
147
|
+
}, { parent: this });
|
|
148
|
+
return {
|
|
149
|
+
lb,
|
|
150
|
+
lbTargetGroup,
|
|
151
|
+
lbHttpListener,
|
|
152
|
+
lbSecurityGroup,
|
|
153
|
+
};
|
|
154
|
+
}
|
|
155
|
+
createSecurityGroup(vpcId) {
|
|
156
|
+
const securityGroup = new aws.ec2.SecurityGroup(`${this.name}-security-group`, {
|
|
157
|
+
vpcId,
|
|
158
|
+
ingress: [
|
|
159
|
+
{
|
|
160
|
+
fromPort: 0,
|
|
161
|
+
toPort: 0,
|
|
162
|
+
protocol: '-1',
|
|
163
|
+
securityGroups: [this.lbSecurityGroup.id],
|
|
164
|
+
},
|
|
165
|
+
],
|
|
166
|
+
egress: [
|
|
167
|
+
{
|
|
168
|
+
fromPort: 0,
|
|
169
|
+
toPort: 0,
|
|
170
|
+
protocol: '-1',
|
|
171
|
+
cidrBlocks: ['0.0.0.0/0'],
|
|
172
|
+
},
|
|
173
|
+
],
|
|
174
|
+
tags: constants_1.commonTags,
|
|
175
|
+
}, { parent: this });
|
|
176
|
+
return securityGroup;
|
|
177
|
+
}
|
|
178
|
+
createEcsService(args) {
|
|
179
|
+
const service = new ecs_service_1.EcsService(this.name, Object.assign(Object.assign({}, args), { enableServiceAutoDiscovery: false, lbTargetGroupArn: this.lbTargetGroup.arn, assignPublicIp: true, subnetIds: args.publicSubnetIds, securityGroup: this.serviceSecurityGroup }), {
|
|
180
|
+
parent: this,
|
|
181
|
+
dependsOn: [this.lb, this.lbTargetGroup, this.lbHttpListener],
|
|
182
|
+
});
|
|
183
|
+
return service;
|
|
184
|
+
}
|
|
185
|
+
createCloudfrontDistribution({ domain, tags, }) {
|
|
186
|
+
const cachePolicy = new aws.cloudfront.CachePolicy(`${this.name}-cf-cache-policy`, {
|
|
187
|
+
comment: 'This cache policy is managed by Pulumi, changing its values will impact multiple services.',
|
|
188
|
+
defaultTtl: 0,
|
|
189
|
+
maxTtl: 31536000,
|
|
190
|
+
minTtl: 0,
|
|
191
|
+
parametersInCacheKeyAndForwardedToOrigin: {
|
|
192
|
+
cookiesConfig: {
|
|
193
|
+
cookieBehavior: 'none',
|
|
194
|
+
},
|
|
195
|
+
headersConfig: {
|
|
196
|
+
headerBehavior: 'none',
|
|
197
|
+
},
|
|
198
|
+
queryStringsConfig: {
|
|
199
|
+
queryStringBehavior: 'all',
|
|
200
|
+
},
|
|
201
|
+
},
|
|
202
|
+
}, { parent: this });
|
|
203
|
+
const originRequestPolicyId = aws.cloudfront
|
|
204
|
+
.getOriginRequestPolicyOutput({
|
|
205
|
+
name: 'Managed-AllViewer',
|
|
206
|
+
})
|
|
207
|
+
.apply(policy => policy.id);
|
|
208
|
+
const responseHeadersPolicyId = aws.cloudfront
|
|
209
|
+
.getResponseHeadersPolicyOutput({
|
|
210
|
+
name: 'Managed-SecurityHeadersPolicy',
|
|
211
|
+
})
|
|
212
|
+
.apply(policy => policy.id);
|
|
213
|
+
const cloudfront = new aws.cloudfront.Distribution(`${this.name}-cloudfront`, Object.assign(Object.assign({ enabled: true }, (domain && { aliases: [domain] })), { isIpv6Enabled: true, waitForDeployment: true, httpVersion: 'http2and3', viewerCertificate: Object.assign({}, (this.certificate
|
|
214
|
+
? {
|
|
215
|
+
acmCertificateArn: this.certificate.certificate.arn,
|
|
216
|
+
sslSupportMethod: 'sni-only',
|
|
217
|
+
minimumProtocolVersion: 'TLSv1.2_2021',
|
|
218
|
+
}
|
|
219
|
+
: {
|
|
220
|
+
cloudfrontDefaultCertificate: true,
|
|
221
|
+
})), origins: [
|
|
222
|
+
{
|
|
223
|
+
originId: this.lb.arn,
|
|
224
|
+
domainName: this.lb.dnsName,
|
|
225
|
+
connectionAttempts: 3,
|
|
226
|
+
connectionTimeout: 10,
|
|
227
|
+
customOriginConfig: {
|
|
228
|
+
originProtocolPolicy: 'http-only',
|
|
229
|
+
httpPort: 80,
|
|
230
|
+
httpsPort: 443,
|
|
231
|
+
originSslProtocols: ['SSLv3'],
|
|
232
|
+
},
|
|
233
|
+
customHeaders: [
|
|
234
|
+
{ name: 'X-Forwarded-Port', value: '443' },
|
|
235
|
+
{ name: 'X-Forwarded-Ssl', value: 'on' },
|
|
236
|
+
this.customCFHeader,
|
|
237
|
+
],
|
|
238
|
+
},
|
|
239
|
+
], defaultCacheBehavior: {
|
|
240
|
+
targetOriginId: this.lb.arn,
|
|
241
|
+
viewerProtocolPolicy: 'redirect-to-https',
|
|
242
|
+
allowedMethods: [
|
|
243
|
+
'GET',
|
|
244
|
+
'HEAD',
|
|
245
|
+
'OPTIONS',
|
|
246
|
+
'PUT',
|
|
247
|
+
'POST',
|
|
248
|
+
'PATCH',
|
|
249
|
+
'DELETE',
|
|
250
|
+
],
|
|
251
|
+
cachedMethods: ['GET', 'HEAD'],
|
|
252
|
+
compress: true,
|
|
253
|
+
cachePolicyId: cachePolicy.id,
|
|
254
|
+
originRequestPolicyId,
|
|
255
|
+
responseHeadersPolicyId,
|
|
256
|
+
}, priceClass: 'PriceClass_100', restrictions: {
|
|
257
|
+
geoRestriction: { restrictionType: 'none' },
|
|
258
|
+
}, tags: Object.assign(Object.assign({}, constants_1.commonTags), tags) }), { parent: this });
|
|
259
|
+
return cloudfront;
|
|
260
|
+
}
|
|
261
|
+
createDnsRecord({ domain, hostedZoneId, }) {
|
|
262
|
+
const cdnAliasRecord = new aws.route53.Record(`${this.name}-cdn-route53-record`, {
|
|
263
|
+
type: 'A',
|
|
264
|
+
name: domain,
|
|
265
|
+
zoneId: hostedZoneId,
|
|
266
|
+
aliases: [
|
|
267
|
+
{
|
|
268
|
+
name: this.cloudfront.domainName,
|
|
269
|
+
zoneId: this.cloudfront.hostedZoneId,
|
|
270
|
+
evaluateTargetHealth: true,
|
|
271
|
+
},
|
|
272
|
+
],
|
|
273
|
+
}, { parent: this });
|
|
274
|
+
return cdnAliasRecord;
|
|
275
|
+
}
|
|
276
|
+
}
|
|
277
|
+
exports.NuxtSSR = NuxtSSR;
|
|
@@ -4,10 +4,13 @@ import * as awsx from '@pulumi/awsx';
|
|
|
4
4
|
import * as upstash from '@upstash/pulumi';
|
|
5
5
|
import { Database, DatabaseArgs } from './database';
|
|
6
6
|
import { WebServer, WebServerArgs } from './web-server';
|
|
7
|
+
import { Mongo, MongoArgs } from './mongo';
|
|
7
8
|
import { Redis, RedisArgs } from './redis';
|
|
8
9
|
import { StaticSite, StaticSiteArgs } from './static-site';
|
|
9
10
|
import { Ec2SSMConnect } from './ec2-ssm-connect';
|
|
10
|
-
|
|
11
|
+
import { EcsService, EcsServiceArgs } from './ecs-service';
|
|
12
|
+
import { NuxtSSR, NuxtSSRArgs } from './nuxt-ssr';
|
|
13
|
+
export type Service = Database | Redis | StaticSite | WebServer | NuxtSSR | Mongo | EcsService;
|
|
11
14
|
export type Services = Record<string, Service>;
|
|
12
15
|
type ServiceArgs = {
|
|
13
16
|
/**
|
|
@@ -15,33 +18,44 @@ type ServiceArgs = {
|
|
|
15
18
|
*/
|
|
16
19
|
serviceName: string;
|
|
17
20
|
};
|
|
18
|
-
export type
|
|
21
|
+
export type DatabaseServiceOptions = {
|
|
19
22
|
type: 'DATABASE';
|
|
20
|
-
} & ServiceArgs & Omit<DatabaseArgs, '
|
|
21
|
-
export type
|
|
23
|
+
} & ServiceArgs & Omit<DatabaseArgs, 'vpcId' | 'vpcCidrBlock' | 'isolatedSubnetIds'>;
|
|
24
|
+
export type RedisServiceOptions = {
|
|
22
25
|
type: 'REDIS';
|
|
23
|
-
} & ServiceArgs &
|
|
24
|
-
export type
|
|
26
|
+
} & ServiceArgs & RedisArgs;
|
|
27
|
+
export type StaticSiteServiceOptions = {
|
|
25
28
|
type: 'STATIC_SITE';
|
|
26
|
-
} & ServiceArgs &
|
|
27
|
-
export type
|
|
29
|
+
} & ServiceArgs & StaticSiteArgs;
|
|
30
|
+
export type WebServerServiceOptions = {
|
|
28
31
|
type: 'WEB_SERVER';
|
|
29
32
|
environment?: aws.ecs.KeyValuePair[] | ((services: Services) => aws.ecs.KeyValuePair[]);
|
|
30
33
|
secrets?: aws.ecs.Secret[] | ((services: Services) => aws.ecs.Secret[]);
|
|
31
|
-
} & ServiceArgs & Omit<WebServerArgs, 'cluster' | '
|
|
34
|
+
} & ServiceArgs & Omit<WebServerArgs, 'cluster' | 'vpcId' | 'vpcCidrBlock' | 'publicSubnetIds' | 'environment' | 'secrets'>;
|
|
35
|
+
export type NuxtSSRServiceOptions = {
|
|
36
|
+
type: 'NUXT_SSR';
|
|
37
|
+
environment?: aws.ecs.KeyValuePair[] | ((services: Services) => aws.ecs.KeyValuePair[]);
|
|
38
|
+
secrets?: aws.ecs.Secret[] | ((services: Services) => aws.ecs.Secret[]);
|
|
39
|
+
} & ServiceArgs & Omit<NuxtSSRArgs, 'cluster' | 'vpcId' | 'vpcCidrBlock' | 'publicSubnetIds' | 'environment' | 'secrets'>;
|
|
40
|
+
export type MongoServiceOptions = {
|
|
41
|
+
type: 'MONGO';
|
|
42
|
+
} & ServiceArgs & Omit<MongoArgs, 'cluster' | 'vpcId' | 'vpcCidrBlock' | 'privateSubnetIds' | 'environment' | 'secrets'>;
|
|
43
|
+
export type EcsServiceOptions = {
|
|
44
|
+
type: 'ECS_SERVICE';
|
|
45
|
+
environment?: aws.ecs.KeyValuePair[] | ((services: Services) => aws.ecs.KeyValuePair[]);
|
|
46
|
+
secrets?: aws.ecs.Secret[] | ((services: Services) => aws.ecs.Secret[]);
|
|
47
|
+
} & ServiceArgs & Omit<EcsServiceArgs, 'cluster' | 'vpcId' | 'vpcCidrBlock' | 'subnetIds' | 'environment' | 'secrets'>;
|
|
32
48
|
export type ProjectArgs = {
|
|
33
|
-
services: (
|
|
34
|
-
hostedZoneId?: pulumi.Input<string>;
|
|
49
|
+
services: (DatabaseServiceOptions | RedisServiceOptions | StaticSiteServiceOptions | WebServerServiceOptions | NuxtSSRServiceOptions | MongoServiceOptions | EcsServiceOptions)[];
|
|
35
50
|
enableSSMConnect?: pulumi.Input<boolean>;
|
|
36
51
|
};
|
|
37
|
-
export declare class
|
|
38
|
-
constructor(
|
|
52
|
+
export declare class MissingEcsCluster extends Error {
|
|
53
|
+
constructor();
|
|
39
54
|
}
|
|
40
55
|
export declare class Project extends pulumi.ComponentResource {
|
|
41
56
|
name: string;
|
|
42
57
|
vpc: awsx.ec2.Vpc;
|
|
43
58
|
cluster?: aws.ecs.Cluster;
|
|
44
|
-
hostedZoneId?: pulumi.Input<string>;
|
|
45
59
|
upstashProvider?: upstash.Provider;
|
|
46
60
|
ec2SSMConnect?: Ec2SSMConnect;
|
|
47
61
|
services: Services;
|
|
@@ -49,10 +63,13 @@ export declare class Project extends pulumi.ComponentResource {
|
|
|
49
63
|
private createVpc;
|
|
50
64
|
private createServices;
|
|
51
65
|
private createRedisPrerequisites;
|
|
52
|
-
private
|
|
66
|
+
private createEcsCluster;
|
|
53
67
|
private createDatabaseService;
|
|
54
68
|
private createRedisService;
|
|
55
69
|
private createStaticSiteService;
|
|
56
70
|
private createWebServerService;
|
|
71
|
+
private createNuxtSSRService;
|
|
72
|
+
private createMongoService;
|
|
73
|
+
private createEcsService;
|
|
57
74
|
}
|
|
58
75
|
export {};
|
|
@@ -11,37 +11,39 @@ var __rest = (this && this.__rest) || function (s, e) {
|
|
|
11
11
|
return t;
|
|
12
12
|
};
|
|
13
13
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
14
|
-
exports.Project = exports.
|
|
14
|
+
exports.Project = exports.MissingEcsCluster = void 0;
|
|
15
15
|
const pulumi = require("@pulumi/pulumi");
|
|
16
16
|
const aws = require("@pulumi/aws");
|
|
17
17
|
const awsx = require("@pulumi/awsx");
|
|
18
18
|
const upstash = require("@upstash/pulumi");
|
|
19
19
|
const database_1 = require("./database");
|
|
20
20
|
const web_server_1 = require("./web-server");
|
|
21
|
+
const mongo_1 = require("./mongo");
|
|
21
22
|
const redis_1 = require("./redis");
|
|
22
23
|
const static_site_1 = require("./static-site");
|
|
23
24
|
const ec2_ssm_connect_1 = require("./ec2-ssm-connect");
|
|
24
25
|
const constants_1 = require("../constants");
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
26
|
+
const ecs_service_1 = require("./ecs-service");
|
|
27
|
+
const nuxt_ssr_1 = require("./nuxt-ssr");
|
|
28
|
+
class MissingEcsCluster extends Error {
|
|
29
|
+
constructor() {
|
|
30
|
+
super('Ecs Cluster does not exist');
|
|
29
31
|
this.name = this.constructor.name;
|
|
30
32
|
}
|
|
31
33
|
}
|
|
32
|
-
exports.
|
|
34
|
+
exports.MissingEcsCluster = MissingEcsCluster;
|
|
33
35
|
class Project extends pulumi.ComponentResource {
|
|
34
36
|
constructor(name, args, opts = {}) {
|
|
35
37
|
super('studion:Project', name, {}, opts);
|
|
36
38
|
this.services = {};
|
|
37
|
-
const { services, hostedZoneId } = args;
|
|
38
39
|
this.name = name;
|
|
39
|
-
this.hostedZoneId = hostedZoneId;
|
|
40
40
|
this.vpc = this.createVpc();
|
|
41
|
-
this.createServices(services);
|
|
41
|
+
this.createServices(args.services);
|
|
42
42
|
if (args.enableSSMConnect) {
|
|
43
43
|
this.ec2SSMConnect = new ec2_ssm_connect_1.Ec2SSMConnect(`${name}-ssm-connect`, {
|
|
44
|
-
|
|
44
|
+
vpcId: this.vpc.vpcId,
|
|
45
|
+
privateSubnetId: this.vpc.privateSubnetIds.apply(ids => ids[0]),
|
|
46
|
+
vpcCidrBlock: this.vpc.vpc.cidrBlock,
|
|
45
47
|
});
|
|
46
48
|
}
|
|
47
49
|
this.registerOutputs();
|
|
@@ -62,11 +64,14 @@ class Project extends pulumi.ComponentResource {
|
|
|
62
64
|
}
|
|
63
65
|
createServices(services) {
|
|
64
66
|
const hasRedisService = services.some(it => it.type === 'REDIS');
|
|
65
|
-
const
|
|
67
|
+
const shouldCreateEcsCluster = services.some(it => it.type === 'WEB_SERVER' ||
|
|
68
|
+
it.type === 'NUXT_SSR' ||
|
|
69
|
+
it.type === 'MONGO' ||
|
|
70
|
+
it.type === 'ECS_SERVICE') && !this.cluster;
|
|
66
71
|
if (hasRedisService)
|
|
67
72
|
this.createRedisPrerequisites();
|
|
68
|
-
if (
|
|
69
|
-
this.
|
|
73
|
+
if (shouldCreateEcsCluster)
|
|
74
|
+
this.createEcsCluster();
|
|
70
75
|
services.forEach(it => {
|
|
71
76
|
if (it.type === 'DATABASE')
|
|
72
77
|
this.createDatabaseService(it);
|
|
@@ -76,6 +81,12 @@ class Project extends pulumi.ComponentResource {
|
|
|
76
81
|
this.createStaticSiteService(it);
|
|
77
82
|
if (it.type === 'WEB_SERVER')
|
|
78
83
|
this.createWebServerService(it);
|
|
84
|
+
if (it.type === 'NUXT_SSR')
|
|
85
|
+
this.createNuxtSSRService(it);
|
|
86
|
+
if (it.type === 'MONGO')
|
|
87
|
+
this.createMongoService(it);
|
|
88
|
+
if (it.type === 'ECS_SERVICE')
|
|
89
|
+
this.createEcsService(it);
|
|
79
90
|
});
|
|
80
91
|
}
|
|
81
92
|
createRedisPrerequisites() {
|
|
@@ -85,7 +96,7 @@ class Project extends pulumi.ComponentResource {
|
|
|
85
96
|
apiKey: upstashConfig.requireSecret('apiKey'),
|
|
86
97
|
});
|
|
87
98
|
}
|
|
88
|
-
|
|
99
|
+
createEcsCluster() {
|
|
89
100
|
const stack = pulumi.getStack();
|
|
90
101
|
this.cluster = new aws.ecs.Cluster(`${this.name}-cluster`, {
|
|
91
102
|
name: `${this.name}-${stack}`,
|
|
@@ -94,7 +105,7 @@ class Project extends pulumi.ComponentResource {
|
|
|
94
105
|
}
|
|
95
106
|
createDatabaseService(options) {
|
|
96
107
|
const { serviceName, type } = options, databaseOptions = __rest(options, ["serviceName", "type"]);
|
|
97
|
-
const service = new database_1.Database(serviceName, Object.assign(Object.assign({}, databaseOptions), { vpc: this.vpc }), { parent: this });
|
|
108
|
+
const service = new database_1.Database(serviceName, Object.assign(Object.assign({}, databaseOptions), { vpcId: this.vpc.vpcId, isolatedSubnetIds: this.vpc.isolatedSubnetIds, vpcCidrBlock: this.vpc.vpc.cidrBlock }), { parent: this });
|
|
98
109
|
this.services[serviceName] = service;
|
|
99
110
|
}
|
|
100
111
|
createRedisService(options) {
|
|
@@ -109,20 +120,51 @@ class Project extends pulumi.ComponentResource {
|
|
|
109
120
|
}
|
|
110
121
|
createStaticSiteService(options) {
|
|
111
122
|
const { serviceName } = options, staticSiteOptions = __rest(options, ["serviceName"]);
|
|
112
|
-
const service = new static_site_1.StaticSite(serviceName,
|
|
123
|
+
const service = new static_site_1.StaticSite(serviceName, staticSiteOptions, {
|
|
124
|
+
parent: this,
|
|
125
|
+
});
|
|
113
126
|
this.services[serviceName] = service;
|
|
114
127
|
}
|
|
115
128
|
createWebServerService(options) {
|
|
116
129
|
if (!this.cluster)
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
130
|
+
throw new MissingEcsCluster();
|
|
131
|
+
const { serviceName, environment, secrets } = options, ecsOptions = __rest(options, ["serviceName", "environment", "secrets"]);
|
|
132
|
+
const parsedEnv = typeof environment === 'function'
|
|
133
|
+
? environment(this.services)
|
|
134
|
+
: environment;
|
|
135
|
+
const parsedSecrets = typeof secrets === 'function' ? secrets(this.services) : secrets;
|
|
136
|
+
const service = new web_server_1.WebServer(serviceName, Object.assign(Object.assign({}, ecsOptions), { cluster: this.cluster, vpcId: this.vpc.vpcId, vpcCidrBlock: this.vpc.vpc.cidrBlock, publicSubnetIds: this.vpc.publicSubnetIds, environment: parsedEnv, secrets: parsedSecrets }), { parent: this });
|
|
137
|
+
this.services[options.serviceName] = service;
|
|
138
|
+
}
|
|
139
|
+
createNuxtSSRService(options) {
|
|
140
|
+
if (!this.cluster)
|
|
141
|
+
throw new MissingEcsCluster();
|
|
142
|
+
const { serviceName, environment, secrets } = options, ecsOptions = __rest(options, ["serviceName", "environment", "secrets"]);
|
|
143
|
+
const parsedEnv = typeof environment === 'function'
|
|
144
|
+
? environment(this.services)
|
|
145
|
+
: environment;
|
|
146
|
+
const parsedSecrets = typeof secrets === 'function' ? secrets(this.services) : secrets;
|
|
147
|
+
const service = new nuxt_ssr_1.NuxtSSR(serviceName, Object.assign(Object.assign({}, ecsOptions), { cluster: this.cluster, vpcId: this.vpc.vpcId, vpcCidrBlock: this.vpc.vpc.cidrBlock, publicSubnetIds: this.vpc.publicSubnetIds, environment: parsedEnv, secrets: parsedSecrets }), { parent: this });
|
|
148
|
+
this.services[options.serviceName] = service;
|
|
149
|
+
}
|
|
150
|
+
createMongoService(options) {
|
|
151
|
+
if (!this.cluster)
|
|
152
|
+
throw new MissingEcsCluster();
|
|
153
|
+
const { serviceName } = options, mongoOptions = __rest(options, ["serviceName"]);
|
|
154
|
+
const service = new mongo_1.Mongo(serviceName, Object.assign(Object.assign({}, mongoOptions), { cluster: this.cluster, vpcId: this.vpc.vpcId, vpcCidrBlock: this.vpc.vpc.cidrBlock, privateSubnetIds: this.vpc.privateSubnetIds }), { parent: this });
|
|
155
|
+
this.services[options.serviceName] = service;
|
|
156
|
+
}
|
|
157
|
+
createEcsService(options) {
|
|
158
|
+
if (!this.cluster)
|
|
159
|
+
throw new MissingEcsCluster();
|
|
120
160
|
const { serviceName, environment, secrets } = options, ecsOptions = __rest(options, ["serviceName", "environment", "secrets"]);
|
|
121
161
|
const parsedEnv = typeof environment === 'function'
|
|
122
162
|
? environment(this.services)
|
|
123
163
|
: environment;
|
|
124
164
|
const parsedSecrets = typeof secrets === 'function' ? secrets(this.services) : secrets;
|
|
125
|
-
const service = new
|
|
165
|
+
const service = new ecs_service_1.EcsService(serviceName, Object.assign(Object.assign({}, ecsOptions), { cluster: this.cluster, vpcId: this.vpc.vpcId, vpcCidrBlock: this.vpc.vpc.cidrBlock, subnetIds: ecsOptions.assignPublicIp
|
|
166
|
+
? this.vpc.publicSubnetIds
|
|
167
|
+
: this.vpc.privateSubnetIds, environment: parsedEnv, secrets: parsedSecrets }), { parent: this });
|
|
126
168
|
this.services[options.serviceName] = service;
|
|
127
169
|
}
|
|
128
170
|
}
|
|
@@ -1,106 +1,37 @@
|
|
|
1
1
|
import * as pulumi from '@pulumi/pulumi';
|
|
2
2
|
import * as aws from '@pulumi/aws';
|
|
3
|
-
import * as awsx from '@pulumi/awsx';
|
|
4
|
-
import { Size } from '../types/size';
|
|
5
3
|
import { AcmCertificate } from './acm-certificate';
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
*/
|
|
10
|
-
name?: pulumi.Input<string>;
|
|
11
|
-
/**
|
|
12
|
-
* Policy document as a JSON formatted string.
|
|
13
|
-
*/
|
|
14
|
-
policy?: pulumi.Input<string>;
|
|
15
|
-
};
|
|
16
|
-
export type WebServerArgs = {
|
|
17
|
-
/**
|
|
18
|
-
* The ECR image used to start a container.
|
|
19
|
-
*/
|
|
20
|
-
image: pulumi.Input<string>;
|
|
21
|
-
/**
|
|
22
|
-
* Exposed service port.
|
|
23
|
-
*/
|
|
24
|
-
port: pulumi.Input<number>;
|
|
4
|
+
import { EcsService, EcsServiceArgs } from './ecs-service';
|
|
5
|
+
export type WebServerArgs = Pick<EcsServiceArgs, 'image' | 'port' | 'cluster' | 'vpcId' | 'vpcCidrBlock' | 'desiredCount' | 'autoscaling' | 'size' | 'environment' | 'secrets' | 'taskExecutionRoleInlinePolicies' | 'taskRoleInlinePolicies' | 'tags'> & {
|
|
6
|
+
publicSubnetIds: pulumi.Input<pulumi.Input<string>[]>;
|
|
25
7
|
/**
|
|
26
8
|
* The domain which will be used to access the service.
|
|
27
9
|
* The domain or subdomain must belong to the provided hostedZone.
|
|
28
10
|
*/
|
|
29
|
-
domain
|
|
30
|
-
/**
|
|
31
|
-
* The aws.ecs.Cluster resource.
|
|
32
|
-
*/
|
|
33
|
-
cluster: aws.ecs.Cluster;
|
|
11
|
+
domain?: pulumi.Input<string>;
|
|
34
12
|
/**
|
|
35
13
|
* The ID of the hosted zone.
|
|
36
14
|
*/
|
|
37
|
-
hostedZoneId
|
|
38
|
-
/**
|
|
39
|
-
* The awsx.ec2.Vpc resource.
|
|
40
|
-
*/
|
|
41
|
-
vpc: awsx.ec2.Vpc;
|
|
42
|
-
/**
|
|
43
|
-
* Number of instances of the task definition to place and keep running. Defaults to 1.
|
|
44
|
-
*/
|
|
45
|
-
desiredCount?: pulumi.Input<number>;
|
|
46
|
-
/**
|
|
47
|
-
* Min capacity of the scalable target. Defaults to 1.
|
|
48
|
-
*/
|
|
49
|
-
minCount?: pulumi.Input<number>;
|
|
50
|
-
/**
|
|
51
|
-
* Max capacity of the scalable target. Defaults to 10.
|
|
52
|
-
*/
|
|
53
|
-
maxCount?: pulumi.Input<number>;
|
|
54
|
-
/**
|
|
55
|
-
* CPU and memory size used for running the container. Defaults to "small".
|
|
56
|
-
* Available predefined options are:
|
|
57
|
-
* - small (0.25 vCPU, 0.5 GB memory)
|
|
58
|
-
* - medium (0.5 vCPU, 1 GB memory)
|
|
59
|
-
* - large (1 vCPU memory, 2 GB memory)
|
|
60
|
-
* - xlarge (2 vCPU, 4 GB memory)
|
|
61
|
-
*/
|
|
62
|
-
size?: pulumi.Input<Size>;
|
|
63
|
-
/**
|
|
64
|
-
* The environment variables to pass to a container. Don't use this field for
|
|
65
|
-
* sensitive information such as passwords, API keys, etc. For that purpose,
|
|
66
|
-
* please use the `secrets` property.
|
|
67
|
-
* Defaults to [].
|
|
68
|
-
*/
|
|
69
|
-
environment?: aws.ecs.KeyValuePair[];
|
|
70
|
-
/**
|
|
71
|
-
* The secrets to pass to the container. Defaults to [].
|
|
72
|
-
*/
|
|
73
|
-
secrets?: aws.ecs.Secret[];
|
|
74
|
-
/**
|
|
75
|
-
* Path for the health check request. Defaults to "/healtcheck".
|
|
76
|
-
*/
|
|
77
|
-
healtCheckPath?: pulumi.Input<string>;
|
|
78
|
-
taskExecutionRoleInlinePolicies?: pulumi.Input<pulumi.Input<RoleInlinePolicy>[]>;
|
|
79
|
-
taskRoleInlinePolicies?: pulumi.Input<pulumi.Input<RoleInlinePolicy>[]>;
|
|
15
|
+
hostedZoneId?: pulumi.Input<string>;
|
|
80
16
|
/**
|
|
81
|
-
*
|
|
17
|
+
* Path for the health check request. Defaults to "/healthcheck".
|
|
82
18
|
*/
|
|
83
|
-
|
|
84
|
-
[key: string]: pulumi.Input<string>;
|
|
85
|
-
}>;
|
|
19
|
+
healthCheckPath?: pulumi.Input<string>;
|
|
86
20
|
};
|
|
87
21
|
export declare class WebServer extends pulumi.ComponentResource {
|
|
88
22
|
name: string;
|
|
89
|
-
|
|
90
|
-
logGroup: aws.cloudwatch.LogGroup;
|
|
23
|
+
service: EcsService;
|
|
91
24
|
lbSecurityGroup: aws.ec2.SecurityGroup;
|
|
25
|
+
serviceSecurityGroup: aws.ec2.SecurityGroup;
|
|
92
26
|
lb: aws.lb.LoadBalancer;
|
|
93
27
|
lbTargetGroup: aws.lb.TargetGroup;
|
|
94
28
|
lbHttpListener: aws.lb.Listener;
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
service: aws.ecs.Service;
|
|
29
|
+
certificate?: AcmCertificate;
|
|
30
|
+
lbTlsListener?: aws.lb.Listener;
|
|
98
31
|
constructor(name: string, args: WebServerArgs, opts?: pulumi.ComponentResourceOptions);
|
|
99
32
|
private createTlsCertificate;
|
|
100
|
-
private createLogGroup;
|
|
101
33
|
private createLoadBalancer;
|
|
102
|
-
private
|
|
34
|
+
private createSecurityGroup;
|
|
103
35
|
private createEcsService;
|
|
104
36
|
private createDnsRecord;
|
|
105
|
-
private enableAutoscaling;
|
|
106
37
|
}
|