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/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
- return __awaiter(this, void 0, void 0, function* () {
24
- const { folderName, extra, attachRolePolicyArn } = config;
25
- const file = require.resolve(folderName);
26
- const lambdaName = (0, path_1.basename)(folderName);
27
- if (!lambdaName.match(/^[a-zA-Z0-9-_]+$/)) {
28
- throw new Error("Invalid(/^[a-zA-Z0-9-_]+$/) folder name: " + folderName);
29
- }
30
- const { subdomain } = (0, getDomainAndSubdomain_1.getDomainAndSubdomain)(fullyQualifiedDomainName);
31
- const rolename = `${subdomain || "ROOTDOMAIN"}-${lambdaName}-role`;
32
- // Configure IAM so that the AWS Lambda can be run.
33
- const lambdaApiGatewayRole = new aws.iam.Role((0, stack_1.getStackScopedName)(rolename), {
34
- assumeRolePolicy: {
35
- Version: "2012-10-17",
36
- Statement: [
37
- {
38
- Action: "sts:AssumeRole",
39
- Principal: {
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
- // attach AWSLambdaBasicExecutionRole
49
- // https://docs.aws.amazon.com/lambda/latest/dg/lambda-intro-execution-role.html
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
- function createGateway(options, fn) {
77
- return __awaiter(this, void 0, void 0, function* () {
78
- const routes = [];
79
- yield fn(function configureApiGatewayLambda(config) {
80
- return __awaiter(this, void 0, void 0, function* () {
81
- const { method, path } = config;
82
- const lambda = yield createLambda(options.fullyQualifiedDomainName, config);
83
- routes.push({
84
- method: method,
85
- path,
86
- eventHandler: lambda
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
- if (routes.length == 0) {
91
- throw new Error("Warning! No routes were configured");
92
- }
93
- const stageName = domain_1.env;
94
- // Create a public HTTP endpoint (using AWS APIGateway)
95
- const gateway = new apigateway.RestAPI((0, stack_1.getStackScopedName)(options.fullyQualifiedDomainName.replace(/\./g, "-")), {
96
- routes: routes,
97
- stageName
98
- });
99
- new aws.apigateway.Stage((0, stack_1.getStackScopedName)(`${options.fullyQualifiedDomainName.replace(/\./g, "-")}-stage`), {
100
- restApi: gateway.api.id,
101
- deployment: gateway.deployment.id,
102
- stageName,
103
- xrayTracingEnabled: true
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
- return __awaiter(this, void 0, void 0, function* () {
116
- const domainParts = (0, getDomainAndSubdomain_1.getDomainAndSubdomain)(fullyQualifiedDomainName);
117
- const subdomain = domainParts.subdomain || "ROOTDOMAIN";
118
- // Configure an edge-optimized domain for our API Gateway. This will configure a Cloudfront CDN
119
- // distribution behind the scenes and serve our API Gateway at a custom domain name over SSL.
120
- const webDomain = new aws.apigateway.DomainName((0, stack_1.getStackScopedName)(subdomain + "-dn"), {
121
- certificateArn: (0, certificate_1.getCertificateFor)(fullyQualifiedDomainName),
122
- domainName: fullyQualifiedDomainName,
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": "8.3.3",
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": "^13.0.0",
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": "^22.0.8",
30
+ "semantic-release": "^24.2.9",
27
31
  "typescript": "5.3.2"
28
32
  },
29
33
  "dependencies": {
30
- "@pulumi/aws": "6.9.0",
31
- "@pulumi/aws-apigateway": "^2.0.1",
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.2.0",
37
+ "@pulumi/awsx": "2.22.0",
34
38
  "@pulumi/cloudflare": "5.15.0",
35
- "@pulumi/docker": "4.5.0",
36
- "@pulumi/pulumi": "3.94.2",
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)(() => __awaiter(void 0, void 0, void 0, function* () {
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)(() => __awaiter(void 0, void 0, void 0, function* () {
22
- const prom = yield (0, exports.prometheusStack)();
23
- return (yield prom.requireOutputValue("prometheusSecurityGroupId"));
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
@@ -0,0 +1,4 @@
1
+ import * as pulumi from "@pulumi/pulumi";
2
+ export declare const slackStack: () => Promise<pulumi.StackReference>;
3
+ export declare const getSlackNotificationTopicArn: () => Promise<string>;
4
+ export declare const getSlackNotificationLambdaArn: () => Promise<string>;