dcl-ops-lib 8.3.3 → 9.1.0
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/acceptBastion.js +3 -12
- package/acceptDb.js +6 -15
- package/accessTheInternet.js +23 -34
- package/alb.js +7 -16
- package/buildStatic.js +1 -2
- package/cloudflare.js +47 -57
- package/createFargateTask.d.ts +3 -2
- package/createFargateTask.js +233 -252
- package/createImageFromContext.js +6 -2
- package/createSQSTriggeredFargateTask.d.ts +63 -0
- package/createSQSTriggeredFargateTask.js +110 -0
- package/createScheduledFargateTask.d.ts +75 -0
- package/createScheduledFargateTask.js +108 -0
- package/exposePublicService.js +74 -85
- package/fargateHelpers.d.ts +220 -0
- package/fargateHelpers.js +178 -0
- package/getAmi.js +14 -25
- package/getImageRegistryAndCredentials.js +3 -12
- package/index.d.ts +32 -0
- package/index.js +52 -0
- package/lambda.js +124 -132
- package/package.json +13 -9
- package/prometheus.js +6 -15
- package/scheduledTaskBase.d.ts +142 -0
- package/scheduledTaskBase.js +146 -0
- package/slack.d.ts +4 -0
- package/slack.js +21 -0
- package/utils.js +1 -1
- package/values.js +12 -23
- package/vpc.js +7 -18
package/lambda.js
CHANGED
|
@@ -1,13 +1,4 @@
|
|
|
1
1
|
"use strict";
|
|
2
|
-
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
|
3
|
-
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
4
|
-
return new (P || (P = Promise))(function (resolve, reject) {
|
|
5
|
-
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
|
6
|
-
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
|
7
|
-
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
|
8
|
-
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
9
|
-
});
|
|
10
|
-
};
|
|
11
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
12
3
|
exports.createGateway = void 0;
|
|
13
4
|
const aws = require("@pulumi/aws");
|
|
@@ -19,137 +10,138 @@ const cloudflare_1 = require("./cloudflare");
|
|
|
19
10
|
const getDomainAndSubdomain_1 = require("./getDomainAndSubdomain");
|
|
20
11
|
const certificate_1 = require("./certificate");
|
|
21
12
|
const stack_1 = require("./stack");
|
|
22
|
-
function createLambda(fullyQualifiedDomainName, config) {
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
Service: "lambda.amazonaws.com",
|
|
41
|
-
},
|
|
42
|
-
Effect: "Allow",
|
|
43
|
-
Sid: "",
|
|
13
|
+
async function createLambda(fullyQualifiedDomainName, config) {
|
|
14
|
+
const { folderName, extra, attachRolePolicyArn } = config;
|
|
15
|
+
const file = require.resolve(folderName);
|
|
16
|
+
const lambdaName = (0, path_1.basename)(folderName);
|
|
17
|
+
if (!lambdaName.match(/^[a-zA-Z0-9-_]+$/)) {
|
|
18
|
+
throw new Error("Invalid(/^[a-zA-Z0-9-_]+$/) folder name: " + folderName);
|
|
19
|
+
}
|
|
20
|
+
const { subdomain } = (0, getDomainAndSubdomain_1.getDomainAndSubdomain)(fullyQualifiedDomainName);
|
|
21
|
+
const rolename = `${subdomain || "ROOTDOMAIN"}-${lambdaName}-role`;
|
|
22
|
+
// Configure IAM so that the AWS Lambda can be run.
|
|
23
|
+
const lambdaApiGatewayRole = new aws.iam.Role((0, stack_1.getStackScopedName)(rolename), {
|
|
24
|
+
assumeRolePolicy: {
|
|
25
|
+
Version: "2012-10-17",
|
|
26
|
+
Statement: [
|
|
27
|
+
{
|
|
28
|
+
Action: "sts:AssumeRole",
|
|
29
|
+
Principal: {
|
|
30
|
+
Service: "lambda.amazonaws.com",
|
|
44
31
|
},
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
// write cloudwatch logs
|
|
51
|
-
new aws.iam.RolePolicyAttachment((0, stack_1.getStackScopedName)(`${rolename}-AWSLambdaBasicExecutionRole`), {
|
|
52
|
-
role: lambdaApiGatewayRole,
|
|
53
|
-
policyArn: aws.iam.ManagedPolicies.AWSLambdaBasicExecutionRole,
|
|
54
|
-
});
|
|
55
|
-
// upload xray traces
|
|
56
|
-
new aws.iam.RolePolicyAttachment((0, stack_1.getStackScopedName)(`${rolename}-AWSXRayDaemonWriteAccess`), {
|
|
57
|
-
role: lambdaApiGatewayRole,
|
|
58
|
-
policyArn: aws.iam.ManagedPolicies.AWSXRayDaemonWriteAccess,
|
|
59
|
-
});
|
|
60
|
-
if (attachRolePolicyArn) {
|
|
61
|
-
Object.entries(attachRolePolicyArn).forEach(([name, policyArn]) => {
|
|
62
|
-
new aws.iam.RolePolicyAttachment((0, stack_1.getStackScopedName)(`${rolename}-${name}`), {
|
|
63
|
-
role: lambdaApiGatewayRole,
|
|
64
|
-
policyArn,
|
|
65
|
-
});
|
|
66
|
-
});
|
|
67
|
-
}
|
|
68
|
-
const name = (0, stack_1.getStackScopedName)((subdomain || "ROOTDOMAIN") + "-" + lambdaName);
|
|
69
|
-
const lambda = new aws.lambda.Function(name, Object.assign({ name: name, handler: `${(0, path_1.basename)(file, ".js")}.handler`, timeout: 900, memorySize: 1024, runtime: "nodejs18.x", code: (extra === null || extra === void 0 ? void 0 : extra.code) ||
|
|
70
|
-
new pulumi.asset.AssetArchive({
|
|
71
|
-
[(0, path_1.basename)(file)]: new pulumi.asset.FileAsset(file),
|
|
72
|
-
}), role: (extra === null || extra === void 0 ? void 0 : extra.role) || lambdaApiGatewayRole.arn }, extra));
|
|
73
|
-
return lambda;
|
|
32
|
+
Effect: "Allow",
|
|
33
|
+
Sid: "",
|
|
34
|
+
},
|
|
35
|
+
],
|
|
36
|
+
},
|
|
74
37
|
});
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
38
|
+
// attach AWSLambdaBasicExecutionRole
|
|
39
|
+
// https://docs.aws.amazon.com/lambda/latest/dg/lambda-intro-execution-role.html
|
|
40
|
+
// write cloudwatch logs
|
|
41
|
+
new aws.iam.RolePolicyAttachment((0, stack_1.getStackScopedName)(`${rolename}-AWSLambdaBasicExecutionRole`), {
|
|
42
|
+
role: lambdaApiGatewayRole,
|
|
43
|
+
policyArn: aws.iam.ManagedPolicies.AWSLambdaBasicExecutionRole,
|
|
44
|
+
});
|
|
45
|
+
// upload xray traces
|
|
46
|
+
new aws.iam.RolePolicyAttachment((0, stack_1.getStackScopedName)(`${rolename}-AWSXRayDaemonWriteAccess`), {
|
|
47
|
+
role: lambdaApiGatewayRole,
|
|
48
|
+
policyArn: aws.iam.ManagedPolicies.AWSXRayDaemonWriteAccess,
|
|
49
|
+
});
|
|
50
|
+
if (attachRolePolicyArn) {
|
|
51
|
+
Object.entries(attachRolePolicyArn).forEach(([name, policyArn]) => {
|
|
52
|
+
new aws.iam.RolePolicyAttachment((0, stack_1.getStackScopedName)(`${rolename}-${name}`), {
|
|
53
|
+
role: lambdaApiGatewayRole,
|
|
54
|
+
policyArn,
|
|
88
55
|
});
|
|
89
56
|
});
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
57
|
+
}
|
|
58
|
+
const name = (0, stack_1.getStackScopedName)((subdomain || "ROOTDOMAIN") + "-" + lambdaName);
|
|
59
|
+
const lambda = new aws.lambda.Function(name, {
|
|
60
|
+
name: name,
|
|
61
|
+
handler: `${(0, path_1.basename)(file, ".js")}.handler`,
|
|
62
|
+
timeout: 900,
|
|
63
|
+
memorySize: 1024,
|
|
64
|
+
runtime: "nodejs18.x",
|
|
65
|
+
code: extra?.code ||
|
|
66
|
+
new pulumi.asset.AssetArchive({
|
|
67
|
+
[(0, path_1.basename)(file)]: new pulumi.asset.FileAsset(file),
|
|
68
|
+
}),
|
|
69
|
+
role: extra?.role || lambdaApiGatewayRole.arn,
|
|
70
|
+
...extra,
|
|
71
|
+
});
|
|
72
|
+
return lambda;
|
|
73
|
+
}
|
|
74
|
+
async function createGateway(options, fn) {
|
|
75
|
+
const routes = [];
|
|
76
|
+
await fn(async function configureApiGatewayLambda(config) {
|
|
77
|
+
const { method, path } = config;
|
|
78
|
+
const lambda = await createLambda(options.fullyQualifiedDomainName, config);
|
|
79
|
+
routes.push({
|
|
80
|
+
method: method,
|
|
81
|
+
path,
|
|
82
|
+
eventHandler: lambda
|
|
104
83
|
});
|
|
105
|
-
const { record, lambdasDomain } = yield configureApiGatewayDomain(options.fullyQualifiedDomainName, gateway);
|
|
106
|
-
return {
|
|
107
|
-
gateway,
|
|
108
|
-
record,
|
|
109
|
-
lambdasDomain,
|
|
110
|
-
};
|
|
111
84
|
});
|
|
85
|
+
if (routes.length == 0) {
|
|
86
|
+
throw new Error("Warning! No routes were configured");
|
|
87
|
+
}
|
|
88
|
+
const stageName = domain_1.env;
|
|
89
|
+
// Create a public HTTP endpoint (using AWS APIGateway)
|
|
90
|
+
const gateway = new apigateway.RestAPI((0, stack_1.getStackScopedName)(options.fullyQualifiedDomainName.replace(/\./g, "-")), {
|
|
91
|
+
routes: routes,
|
|
92
|
+
stageName
|
|
93
|
+
});
|
|
94
|
+
new aws.apigateway.Stage((0, stack_1.getStackScopedName)(`${options.fullyQualifiedDomainName.replace(/\./g, "-")}-stage`), {
|
|
95
|
+
restApi: gateway.api.id,
|
|
96
|
+
deployment: gateway.deployment.id,
|
|
97
|
+
stageName,
|
|
98
|
+
xrayTracingEnabled: true
|
|
99
|
+
});
|
|
100
|
+
const { record, lambdasDomain } = await configureApiGatewayDomain(options.fullyQualifiedDomainName, gateway);
|
|
101
|
+
return {
|
|
102
|
+
gateway,
|
|
103
|
+
record,
|
|
104
|
+
lambdasDomain,
|
|
105
|
+
};
|
|
112
106
|
}
|
|
113
107
|
exports.createGateway = createGateway;
|
|
114
|
-
function configureApiGatewayDomain(fullyQualifiedDomainName, gateway) {
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
deleteBeforeReplace: true,
|
|
125
|
-
});
|
|
126
|
-
const webDomainMapping = new aws.apigateway.BasePathMapping((0, stack_1.getStackScopedName)(subdomain + "-bpm"), {
|
|
127
|
-
restApi: gateway.api.id,
|
|
128
|
-
stageName: gateway.stage.stageName,
|
|
129
|
-
domainName: webDomain.id,
|
|
130
|
-
}, { dependsOn: [webDomain], deleteBeforeReplace: true });
|
|
131
|
-
const hostedZone = yield aws.route53.getZone({ name: domainParts.parentDomain }, { async: true });
|
|
132
|
-
const record = new aws.route53.Record((0, stack_1.getStackScopedName)(fullyQualifiedDomainName), {
|
|
133
|
-
name: domainParts.subdomain || "",
|
|
134
|
-
zoneId: hostedZone.zoneId,
|
|
135
|
-
type: "A",
|
|
136
|
-
aliases: [
|
|
137
|
-
{
|
|
138
|
-
evaluateTargetHealth: true,
|
|
139
|
-
name: webDomain.cloudfrontDomainName,
|
|
140
|
-
zoneId: webDomain.cloudfrontZoneId,
|
|
141
|
-
},
|
|
142
|
-
],
|
|
143
|
-
}, { dependsOn: [webDomainMapping] });
|
|
144
|
-
if (domainParts.parentDomain.replace(/\.$/, "") == domain_1.publicDomain) {
|
|
145
|
-
(0, cloudflare_1.setRecord)({
|
|
146
|
-
proxied: true,
|
|
147
|
-
recordName: domainParts.subdomain,
|
|
148
|
-
type: "CNAME",
|
|
149
|
-
value: webDomain.cloudfrontDomainName,
|
|
150
|
-
});
|
|
151
|
-
}
|
|
152
|
-
return { record, lambdasDomain: fullyQualifiedDomainName };
|
|
108
|
+
async function configureApiGatewayDomain(fullyQualifiedDomainName, gateway) {
|
|
109
|
+
const domainParts = (0, getDomainAndSubdomain_1.getDomainAndSubdomain)(fullyQualifiedDomainName);
|
|
110
|
+
const subdomain = domainParts.subdomain || "ROOTDOMAIN";
|
|
111
|
+
// Configure an edge-optimized domain for our API Gateway. This will configure a Cloudfront CDN
|
|
112
|
+
// distribution behind the scenes and serve our API Gateway at a custom domain name over SSL.
|
|
113
|
+
const webDomain = new aws.apigateway.DomainName((0, stack_1.getStackScopedName)(subdomain + "-dn"), {
|
|
114
|
+
certificateArn: (0, certificate_1.getCertificateFor)(fullyQualifiedDomainName),
|
|
115
|
+
domainName: fullyQualifiedDomainName,
|
|
116
|
+
}, {
|
|
117
|
+
deleteBeforeReplace: true,
|
|
153
118
|
});
|
|
119
|
+
const webDomainMapping = new aws.apigateway.BasePathMapping((0, stack_1.getStackScopedName)(subdomain + "-bpm"), {
|
|
120
|
+
restApi: gateway.api.id,
|
|
121
|
+
stageName: gateway.stage.stageName,
|
|
122
|
+
domainName: webDomain.id,
|
|
123
|
+
}, { dependsOn: [webDomain], deleteBeforeReplace: true });
|
|
124
|
+
const hostedZone = await aws.route53.getZone({ name: domainParts.parentDomain }, { async: true });
|
|
125
|
+
const record = new aws.route53.Record((0, stack_1.getStackScopedName)(fullyQualifiedDomainName), {
|
|
126
|
+
name: domainParts.subdomain || "",
|
|
127
|
+
zoneId: hostedZone.zoneId,
|
|
128
|
+
type: "A",
|
|
129
|
+
aliases: [
|
|
130
|
+
{
|
|
131
|
+
evaluateTargetHealth: true,
|
|
132
|
+
name: webDomain.cloudfrontDomainName,
|
|
133
|
+
zoneId: webDomain.cloudfrontZoneId,
|
|
134
|
+
},
|
|
135
|
+
],
|
|
136
|
+
}, { dependsOn: [webDomainMapping] });
|
|
137
|
+
if (domainParts.parentDomain.replace(/\.$/, "") == domain_1.publicDomain) {
|
|
138
|
+
(0, cloudflare_1.setRecord)({
|
|
139
|
+
proxied: true,
|
|
140
|
+
recordName: domainParts.subdomain,
|
|
141
|
+
type: "CNAME",
|
|
142
|
+
value: webDomain.cloudfrontDomainName,
|
|
143
|
+
});
|
|
144
|
+
}
|
|
145
|
+
return { record, lambdasDomain: fullyQualifiedDomainName };
|
|
154
146
|
}
|
|
155
147
|
//# sourceMappingURL=lambda.js.map
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "dcl-ops-lib",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "9.1.0",
|
|
4
4
|
"scripts": {
|
|
5
5
|
"build": "tsc && cp bin/* . && node test.js",
|
|
6
6
|
"clean": "rm *.d.ts *.js *.js.map"
|
|
@@ -15,25 +15,29 @@
|
|
|
15
15
|
"main": "index.js",
|
|
16
16
|
"release": {
|
|
17
17
|
"branches": [
|
|
18
|
-
"master"
|
|
18
|
+
"master",
|
|
19
|
+
{
|
|
20
|
+
"name": "next",
|
|
21
|
+
"prerelease": true
|
|
22
|
+
}
|
|
19
23
|
],
|
|
20
24
|
"extends": "@semantic-release/gitlab-config"
|
|
21
25
|
},
|
|
22
26
|
"devDependencies": {
|
|
23
|
-
"@semantic-release/gitlab-config": "^
|
|
27
|
+
"@semantic-release/gitlab-config": "^14.0.1",
|
|
24
28
|
"@types/mime": "^3.0.4",
|
|
25
29
|
"@types/node": "20.9.3",
|
|
26
|
-
"semantic-release": "^
|
|
30
|
+
"semantic-release": "^24.2.9",
|
|
27
31
|
"typescript": "5.3.2"
|
|
28
32
|
},
|
|
29
33
|
"dependencies": {
|
|
30
|
-
"@pulumi/aws": "6.
|
|
31
|
-
"@pulumi/aws-apigateway": "
|
|
34
|
+
"@pulumi/aws": "6.83.2",
|
|
35
|
+
"@pulumi/aws-apigateway": "2.6.3",
|
|
32
36
|
"@pulumi/aws-native": "^0.86.0",
|
|
33
|
-
"@pulumi/awsx": "2.
|
|
37
|
+
"@pulumi/awsx": "2.22.0",
|
|
34
38
|
"@pulumi/cloudflare": "5.15.0",
|
|
35
|
-
"@pulumi/docker": "4.
|
|
36
|
-
"@pulumi/pulumi": "3.
|
|
39
|
+
"@pulumi/docker": "4.11.0",
|
|
40
|
+
"@pulumi/pulumi": "3.212.0",
|
|
37
41
|
"mime": "^3.0.0"
|
|
38
42
|
}
|
|
39
43
|
}
|
package/prometheus.js
CHANGED
|
@@ -1,13 +1,4 @@
|
|
|
1
1
|
"use strict";
|
|
2
|
-
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
|
3
|
-
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
4
|
-
return new (P || (P = Promise))(function (resolve, reject) {
|
|
5
|
-
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
|
6
|
-
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
|
7
|
-
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
|
8
|
-
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
9
|
-
});
|
|
10
|
-
};
|
|
11
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
12
3
|
exports.makeSecurityGroupAccessibleByPrometheus = exports.prometheusSecurityGroupId = exports.prometheusStack = void 0;
|
|
13
4
|
const pulumi = require("@pulumi/pulumi");
|
|
@@ -15,13 +6,13 @@ const aws = require("@pulumi/aws");
|
|
|
15
6
|
const domain_1 = require("./domain");
|
|
16
7
|
const withCache_1 = require("./withCache");
|
|
17
8
|
const utils_1 = require("./utils");
|
|
18
|
-
exports.prometheusStack = (0, withCache_1.default)(() =>
|
|
9
|
+
exports.prometheusStack = (0, withCache_1.default)(async () => {
|
|
19
10
|
return new pulumi.StackReference(`prometheus-${domain_1.env}`);
|
|
20
|
-
})
|
|
21
|
-
exports.prometheusSecurityGroupId = (0, withCache_1.default)(() =>
|
|
22
|
-
const prom =
|
|
23
|
-
return (
|
|
24
|
-
})
|
|
11
|
+
});
|
|
12
|
+
exports.prometheusSecurityGroupId = (0, withCache_1.default)(async () => {
|
|
13
|
+
const prom = await (0, exports.prometheusStack)();
|
|
14
|
+
return (await prom.requireOutputValue("prometheusSecurityGroupId"));
|
|
15
|
+
});
|
|
25
16
|
function makeSecurityGroupAccessibleByPrometheus(securityGroup, fromPort = 0, toPort = 0, ruleName = "") {
|
|
26
17
|
new aws.ec2.SecurityGroupRule((0, utils_1.withRuleName)("allow-prometheus", ruleName), {
|
|
27
18
|
sourceSecurityGroupId: (0, exports.prometheusSecurityGroupId)(),
|
|
@@ -0,0 +1,142 @@
|
|
|
1
|
+
import * as aws from "@pulumi/aws";
|
|
2
|
+
import * as pulumi from "@pulumi/pulumi";
|
|
3
|
+
import { Team, MetricsConfig } from "./fargateHelpers";
|
|
4
|
+
export { Team, MetricsConfig };
|
|
5
|
+
/**
|
|
6
|
+
* Retry policy configuration
|
|
7
|
+
*/
|
|
8
|
+
export type RetryPolicyConfig = {
|
|
9
|
+
/**
|
|
10
|
+
* Maximum number of retry attempts (0-185)
|
|
11
|
+
*/
|
|
12
|
+
maxRetries?: number;
|
|
13
|
+
/**
|
|
14
|
+
* Maximum age of the event in seconds before it's discarded
|
|
15
|
+
*/
|
|
16
|
+
maxAgeSeconds?: number;
|
|
17
|
+
};
|
|
18
|
+
/**
|
|
19
|
+
* Base options shared between scheduled and SQS-triggered tasks
|
|
20
|
+
*/
|
|
21
|
+
export type BaseFargateTaskOptions = {
|
|
22
|
+
/**
|
|
23
|
+
* Team that owns this task
|
|
24
|
+
*/
|
|
25
|
+
team: Team;
|
|
26
|
+
/**
|
|
27
|
+
* CPU units for the task (256, 512, 1024, 2048, 4096)
|
|
28
|
+
*/
|
|
29
|
+
cpu?: number;
|
|
30
|
+
/**
|
|
31
|
+
* Memory in MB for the task (512 - 30720, depending on CPU)
|
|
32
|
+
*/
|
|
33
|
+
memory?: number;
|
|
34
|
+
/**
|
|
35
|
+
* Ephemeral storage in GB (20-200)
|
|
36
|
+
*/
|
|
37
|
+
ephemeralStorageInGB?: number;
|
|
38
|
+
/**
|
|
39
|
+
* Environment variables for the container
|
|
40
|
+
*/
|
|
41
|
+
environment?: {
|
|
42
|
+
name: string;
|
|
43
|
+
value: pulumi.Input<string>;
|
|
44
|
+
}[];
|
|
45
|
+
/**
|
|
46
|
+
* Secrets from SSM/Secrets Manager
|
|
47
|
+
*/
|
|
48
|
+
secrets?: aws.ecs.Secret[];
|
|
49
|
+
/**
|
|
50
|
+
* Override the container's default command
|
|
51
|
+
*/
|
|
52
|
+
command?: string[];
|
|
53
|
+
/**
|
|
54
|
+
* Override the container's entry point
|
|
55
|
+
*/
|
|
56
|
+
entryPoint?: string[];
|
|
57
|
+
/**
|
|
58
|
+
* Additional security groups
|
|
59
|
+
*/
|
|
60
|
+
securityGroups?: (string | pulumi.Output<string>)[];
|
|
61
|
+
/**
|
|
62
|
+
* Whether to assign a public IP (default: false)
|
|
63
|
+
*/
|
|
64
|
+
assignPublicIp?: boolean;
|
|
65
|
+
/**
|
|
66
|
+
* Prometheus metrics configuration
|
|
67
|
+
*/
|
|
68
|
+
metrics?: MetricsConfig;
|
|
69
|
+
/**
|
|
70
|
+
* CloudWatch log retention in days (default: 60)
|
|
71
|
+
*/
|
|
72
|
+
logRetentionDays?: number;
|
|
73
|
+
/**
|
|
74
|
+
* Policies for the task execution role
|
|
75
|
+
*/
|
|
76
|
+
executionRolePolicies?: Record<string, pulumi.Input<string> | aws.iam.Policy>;
|
|
77
|
+
/**
|
|
78
|
+
* Policies for the task role (application permissions)
|
|
79
|
+
*/
|
|
80
|
+
taskRolePolicies?: Record<string, pulumi.Input<string> | aws.iam.Policy>;
|
|
81
|
+
/**
|
|
82
|
+
* ECS cluster to run the task on
|
|
83
|
+
*/
|
|
84
|
+
cluster?: aws.ecs.Cluster | string;
|
|
85
|
+
/**
|
|
86
|
+
* Number of tasks to launch per invocation (default: 1)
|
|
87
|
+
*/
|
|
88
|
+
taskCount?: number;
|
|
89
|
+
/**
|
|
90
|
+
* Runtime platform configuration (e.g., for ARM64)
|
|
91
|
+
*/
|
|
92
|
+
runtimePlatform?: aws.types.input.ecs.TaskDefinitionRuntimePlatform;
|
|
93
|
+
/**
|
|
94
|
+
* Volumes to attach to the task
|
|
95
|
+
*/
|
|
96
|
+
volumes?: pulumi.Input<aws.types.input.ecs.TaskDefinitionVolume[]>;
|
|
97
|
+
/**
|
|
98
|
+
* Mount points for the container
|
|
99
|
+
*/
|
|
100
|
+
mountPoints?: aws.ecs.MountPoint[];
|
|
101
|
+
/**
|
|
102
|
+
* Repository credentials for private registries
|
|
103
|
+
*/
|
|
104
|
+
repositoryCredentials?: aws.ecs.RepositoryCredentials;
|
|
105
|
+
/**
|
|
106
|
+
* Additional resources to depend on
|
|
107
|
+
*/
|
|
108
|
+
dependsOn?: pulumi.Resource[];
|
|
109
|
+
};
|
|
110
|
+
/**
|
|
111
|
+
* Result of creating the base task infrastructure
|
|
112
|
+
*/
|
|
113
|
+
export type BaseTaskInfrastructure = {
|
|
114
|
+
taskDefinition: aws.ecs.TaskDefinition;
|
|
115
|
+
taskSecurityGroup: aws.ec2.SecurityGroup;
|
|
116
|
+
logGroup: aws.cloudwatch.LogGroup;
|
|
117
|
+
executionRole: aws.iam.Role;
|
|
118
|
+
taskRole: aws.iam.Role;
|
|
119
|
+
eventBridgeRole: aws.iam.Role;
|
|
120
|
+
eventBridgePolicy: aws.iam.RolePolicy;
|
|
121
|
+
clusterArn: string | pulumi.Output<string>;
|
|
122
|
+
subnetIds: string[];
|
|
123
|
+
securityGroups: (string | pulumi.Output<string>)[];
|
|
124
|
+
assignPublicIp: boolean;
|
|
125
|
+
taskCount: number;
|
|
126
|
+
resourcePrefix: string;
|
|
127
|
+
taskName: string;
|
|
128
|
+
team: Team;
|
|
129
|
+
};
|
|
130
|
+
/**
|
|
131
|
+
* Creates the base infrastructure shared by both scheduled and SQS-triggered Fargate tasks.
|
|
132
|
+
* This includes: task definition, security group, log group, and IAM roles.
|
|
133
|
+
*
|
|
134
|
+
* Note: Slack notifications are now handled globally by the ops-lambdas EventBridge rule.
|
|
135
|
+
*
|
|
136
|
+
* @param taskName A name for this task
|
|
137
|
+
* @param dockerImage The docker image to run
|
|
138
|
+
* @param options Base configuration options
|
|
139
|
+
* @param eventBridgeServices AWS services that can assume the EventBridge role
|
|
140
|
+
* @returns Base infrastructure resources
|
|
141
|
+
*/
|
|
142
|
+
export declare function createBaseTaskInfrastructure(taskName: string, dockerImage: string | Promise<string> | pulumi.OutputInstance<string>, options: BaseFargateTaskOptions, eventBridgeServices: string[]): Promise<BaseTaskInfrastructure>;
|
|
@@ -0,0 +1,146 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.createBaseTaskInfrastructure = void 0;
|
|
4
|
+
const aws = require("@pulumi/aws");
|
|
5
|
+
const pulumi = require("@pulumi/pulumi");
|
|
6
|
+
const network_1 = require("./network");
|
|
7
|
+
const vpc_1 = require("./vpc");
|
|
8
|
+
const stack_1 = require("./stack");
|
|
9
|
+
const accessTheInternet_1 = require("./accessTheInternet");
|
|
10
|
+
const createFargateTask_1 = require("./createFargateTask");
|
|
11
|
+
const fargateHelpers_1 = require("./fargateHelpers");
|
|
12
|
+
/**
|
|
13
|
+
* Creates the base infrastructure shared by both scheduled and SQS-triggered Fargate tasks.
|
|
14
|
+
* This includes: task definition, security group, log group, and IAM roles.
|
|
15
|
+
*
|
|
16
|
+
* Note: Slack notifications are now handled globally by the ops-lambdas EventBridge rule.
|
|
17
|
+
*
|
|
18
|
+
* @param taskName A name for this task
|
|
19
|
+
* @param dockerImage The docker image to run
|
|
20
|
+
* @param options Base configuration options
|
|
21
|
+
* @param eventBridgeServices AWS services that can assume the EventBridge role
|
|
22
|
+
* @returns Base infrastructure resources
|
|
23
|
+
*/
|
|
24
|
+
async function createBaseTaskInfrastructure(taskName, dockerImage, options, eventBridgeServices) {
|
|
25
|
+
const { team, cpu = fargateHelpers_1.DEFAULT_CPU, memory = fargateHelpers_1.DEFAULT_MEMORY, ephemeralStorageInGB, environment = [], secrets = [], command, entryPoint, securityGroups = [], assignPublicIp = false, metrics, logRetentionDays = fargateHelpers_1.DEFAULT_LOG_RETENTION_DAYS, executionRolePolicies = {}, taskRolePolicies = {}, cluster, taskCount = fargateHelpers_1.DEFAULT_DESIRED_COUNT, runtimePlatform, volumes, mountPoints = [], repositoryCredentials, dependsOn = [], } = options;
|
|
26
|
+
const version = (0, stack_1.getStackId)();
|
|
27
|
+
const resourcePrefix = `${taskName}-${version}`;
|
|
28
|
+
// Create execution role
|
|
29
|
+
const { role: executionRole, policies: executionPolicies } = (0, createFargateTask_1.getFargateExecutionRole)(`${resourcePrefix}-execution`, executionRolePolicies);
|
|
30
|
+
dependsOn.push(...executionPolicies);
|
|
31
|
+
// Create task role
|
|
32
|
+
const { role: taskRole, policies: taskPolicies } = (0, createFargateTask_1.getFargateTaskRole)(`${resourcePrefix}-task`, taskRolePolicies);
|
|
33
|
+
dependsOn.push(...taskPolicies);
|
|
34
|
+
// Get VPC for security group
|
|
35
|
+
const vpc = await (0, vpc_1.getVpc)();
|
|
36
|
+
// Create security group for the task
|
|
37
|
+
const taskSecurityGroup = (0, fargateHelpers_1.createTaskSecurityGroup)(resourcePrefix, vpc.id, taskName, team);
|
|
38
|
+
// Configure docker labels for Prometheus metrics
|
|
39
|
+
let dockerLabels = {};
|
|
40
|
+
let portMappings = [];
|
|
41
|
+
if (metrics) {
|
|
42
|
+
const prometheusConfig = (0, fargateHelpers_1.configurePrometheusMetrics)(taskName, metrics);
|
|
43
|
+
dockerLabels = prometheusConfig.dockerLabels;
|
|
44
|
+
portMappings = prometheusConfig.portMappings;
|
|
45
|
+
// Setup security group rules for Prometheus access
|
|
46
|
+
(0, fargateHelpers_1.setupPrometheusSecurityRules)(resourcePrefix, taskSecurityGroup, vpc.cidrBlock, prometheusConfig.metricsPort, taskName);
|
|
47
|
+
}
|
|
48
|
+
// Enable egress traffic to the internet
|
|
49
|
+
(0, accessTheInternet_1.makeSecurityGroupAccessTheInternetV2)(taskSecurityGroup, taskName);
|
|
50
|
+
// Create CloudWatch Log Group
|
|
51
|
+
const logGroup = (0, fargateHelpers_1.createLogGroup)(taskName, team, logRetentionDays);
|
|
52
|
+
// Build container definition
|
|
53
|
+
const containerDefinition = (0, fargateHelpers_1.buildContainerDefinition)({
|
|
54
|
+
name: taskName,
|
|
55
|
+
image: dockerImage,
|
|
56
|
+
cpu,
|
|
57
|
+
memory,
|
|
58
|
+
environment,
|
|
59
|
+
secrets,
|
|
60
|
+
command,
|
|
61
|
+
entryPoint,
|
|
62
|
+
portMappings,
|
|
63
|
+
dockerLabels,
|
|
64
|
+
mountPoints,
|
|
65
|
+
repositoryCredentials,
|
|
66
|
+
logConfiguration: (0, createFargateTask_1.getDefaultLogs)(taskName, logGroup),
|
|
67
|
+
});
|
|
68
|
+
// Create ECS Task Definition
|
|
69
|
+
const taskDefinition = (0, fargateHelpers_1.createFargateTaskDefinition)({
|
|
70
|
+
name: taskName,
|
|
71
|
+
executionRoleArn: executionRole.arn,
|
|
72
|
+
taskRoleArn: taskRole.arn,
|
|
73
|
+
containerDefinitions: pulumi.jsonStringify([containerDefinition]),
|
|
74
|
+
team,
|
|
75
|
+
cpu,
|
|
76
|
+
memory,
|
|
77
|
+
ephemeralStorageInGB,
|
|
78
|
+
runtimePlatform,
|
|
79
|
+
volumes,
|
|
80
|
+
dependsOn: [logGroup, ...dependsOn],
|
|
81
|
+
});
|
|
82
|
+
// Get cluster ARN and subnet IDs
|
|
83
|
+
const clusterArn = await (0, createFargateTask_1.getClusterInstance)(cluster);
|
|
84
|
+
const subnetIds = await (0, network_1.getPrivateSubnetIds)();
|
|
85
|
+
// Create IAM role for EventBridge to run ECS tasks
|
|
86
|
+
const eventBridgeRole = new aws.iam.Role(`${resourcePrefix}-eventbridge-role`, {
|
|
87
|
+
assumeRolePolicy: JSON.stringify({
|
|
88
|
+
Version: "2012-10-17",
|
|
89
|
+
Statement: [
|
|
90
|
+
{
|
|
91
|
+
Effect: "Allow",
|
|
92
|
+
Principal: {
|
|
93
|
+
Service: eventBridgeServices,
|
|
94
|
+
},
|
|
95
|
+
Action: "sts:AssumeRole",
|
|
96
|
+
},
|
|
97
|
+
],
|
|
98
|
+
}),
|
|
99
|
+
tags: (0, fargateHelpers_1.createResourceTags)(taskName, team),
|
|
100
|
+
});
|
|
101
|
+
// Create policy for EventBridge to run ECS tasks
|
|
102
|
+
const eventBridgePolicy = new aws.iam.RolePolicy(`${resourcePrefix}-eventbridge-policy`, {
|
|
103
|
+
role: eventBridgeRole.id,
|
|
104
|
+
policy: pulumi
|
|
105
|
+
.all([taskDefinition.arn, executionRole.arn, taskRole.arn])
|
|
106
|
+
.apply(([taskDefArn, execRoleArn, taskRoleArn]) => JSON.stringify({
|
|
107
|
+
Version: "2012-10-17",
|
|
108
|
+
Statement: [
|
|
109
|
+
{
|
|
110
|
+
Effect: "Allow",
|
|
111
|
+
Action: "ecs:RunTask",
|
|
112
|
+
Resource: taskDefArn,
|
|
113
|
+
Condition: {
|
|
114
|
+
ArnLike: {
|
|
115
|
+
"ecs:cluster": clusterArn,
|
|
116
|
+
},
|
|
117
|
+
},
|
|
118
|
+
},
|
|
119
|
+
{
|
|
120
|
+
Effect: "Allow",
|
|
121
|
+
Action: "iam:PassRole",
|
|
122
|
+
Resource: [execRoleArn, taskRoleArn],
|
|
123
|
+
},
|
|
124
|
+
],
|
|
125
|
+
})),
|
|
126
|
+
});
|
|
127
|
+
return {
|
|
128
|
+
taskDefinition,
|
|
129
|
+
taskSecurityGroup,
|
|
130
|
+
logGroup,
|
|
131
|
+
executionRole,
|
|
132
|
+
taskRole,
|
|
133
|
+
eventBridgeRole,
|
|
134
|
+
eventBridgePolicy,
|
|
135
|
+
clusterArn,
|
|
136
|
+
subnetIds,
|
|
137
|
+
securityGroups: [taskSecurityGroup.id, ...securityGroups],
|
|
138
|
+
assignPublicIp,
|
|
139
|
+
taskCount,
|
|
140
|
+
resourcePrefix,
|
|
141
|
+
taskName,
|
|
142
|
+
team,
|
|
143
|
+
};
|
|
144
|
+
}
|
|
145
|
+
exports.createBaseTaskInfrastructure = createBaseTaskInfrastructure;
|
|
146
|
+
//# sourceMappingURL=scheduledTaskBase.js.map
|
package/slack.d.ts
ADDED