dcl-ops-lib 9.6.0 → 9.8.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/createFargateTask.d.ts +54 -18
- package/createFargateTask.js +95 -31
- package/package.json +1 -1
package/createFargateTask.d.ts
CHANGED
|
@@ -12,11 +12,11 @@ export type ALBMapping = {
|
|
|
12
12
|
healthCheck?: Partial<aws.types.input.alb.TargetGroupHealthCheck>;
|
|
13
13
|
extraExposedServiceOptions?: ExtraExposedServiceOptions;
|
|
14
14
|
};
|
|
15
|
-
export declare function getFargateExecutionRole(name: string, policyArnNamedMap: Record<string, pulumi.Input<string> | aws.iam.Policy
|
|
15
|
+
export declare function getFargateExecutionRole(name: string, policyArnNamedMap: Record<string, pulumi.Input<string> | aws.iam.Policy>, existingRole?: aws.iam.Role): {
|
|
16
16
|
role: import("@pulumi/aws/iam/role").Role;
|
|
17
17
|
policies: import("@pulumi/aws/iam/rolePolicyAttachment").RolePolicyAttachment[];
|
|
18
18
|
};
|
|
19
|
-
export declare function getFargateTaskRole(name: string, policyArnNamedMap: Record<string, pulumi.Input<string> | aws.iam.Policy
|
|
19
|
+
export declare function getFargateTaskRole(name: string, policyArnNamedMap: Record<string, pulumi.Input<string> | aws.iam.Policy>, existingRole?: aws.iam.Role): {
|
|
20
20
|
role: import("@pulumi/aws/iam/role").Role;
|
|
21
21
|
policies: import("@pulumi/aws/iam/rolePolicyAttachment").RolePolicyAttachment[];
|
|
22
22
|
};
|
|
@@ -43,6 +43,21 @@ export type FargateTaskOptions = {
|
|
|
43
43
|
nlbMappings?: NLBMapping[];
|
|
44
44
|
executionRolePolicies?: Record<string, pulumi.Input<string> | aws.iam.Policy>;
|
|
45
45
|
taskRolePolicies?: Record<string, pulumi.Input<string> | aws.iam.Policy>;
|
|
46
|
+
/**
|
|
47
|
+
* Optional pre-built IAM role to use as the Fargate task role. When provided,
|
|
48
|
+
* the role is used as-is and `taskRolePolicies` are attached to it. When omitted
|
|
49
|
+
* (default), a new role is created internally — current behavior, fully backward
|
|
50
|
+
* compatible. Use this when callers need the task role ARN to exist before the
|
|
51
|
+
* Fargate task (e.g., to scope a KMS key policy to that exact ARN).
|
|
52
|
+
*/
|
|
53
|
+
taskRole?: aws.iam.Role;
|
|
54
|
+
/**
|
|
55
|
+
* Optional pre-built IAM role to use as the Fargate execution role. When provided,
|
|
56
|
+
* the role is used as-is and `executionRolePolicies` are attached to it. When
|
|
57
|
+
* omitted (default), a new role is created internally — current behavior, fully
|
|
58
|
+
* backward compatible. Symmetric to `taskRole`; same use case.
|
|
59
|
+
*/
|
|
60
|
+
executionRole?: aws.iam.Role;
|
|
46
61
|
secrets?: aws.ecs.Secret[];
|
|
47
62
|
ignoreServiceDiscovery?: boolean;
|
|
48
63
|
team: Team;
|
|
@@ -61,14 +76,7 @@ export type FargateTaskOptions = {
|
|
|
61
76
|
appAutoscaling?: {
|
|
62
77
|
maxCapacity: number;
|
|
63
78
|
minCapacity?: number;
|
|
64
|
-
policy?:
|
|
65
|
-
metricName: string;
|
|
66
|
-
targetValue: number;
|
|
67
|
-
metricDimensionValue: pulumi.Output<string>;
|
|
68
|
-
statistic: "Average" | "Minimum" | "Maximum";
|
|
69
|
-
scaleOutCooldown: number;
|
|
70
|
-
scaleInCooldown: number;
|
|
71
|
-
}[];
|
|
79
|
+
policy?: AppAutoscalingPolicy[];
|
|
72
80
|
scheduleAction?: {
|
|
73
81
|
minCapacity: number;
|
|
74
82
|
maxCapacity: number;
|
|
@@ -84,6 +92,41 @@ export type FargateTaskOptions = {
|
|
|
84
92
|
};
|
|
85
93
|
enableExecuteCommand?: boolean;
|
|
86
94
|
};
|
|
95
|
+
/**
|
|
96
|
+
* One ECS Service Application-Auto-Scaling target-tracking policy.
|
|
97
|
+
*
|
|
98
|
+
* Tracks `metricName` (today only `ApproximateNumberOfMessagesVisible` is
|
|
99
|
+
* understood as an SQS queue depth — anything else falls through with no
|
|
100
|
+
* `customizedMetricSpecification` set) against `targetValue`. Required
|
|
101
|
+
* cooldowns are enforced by AWS.
|
|
102
|
+
*
|
|
103
|
+
* Choose ONE of:
|
|
104
|
+
* - `metricDimensionValue`: a single SQS queue URL. The policy tracks that
|
|
105
|
+
* queue's depth directly.
|
|
106
|
+
* - `metricDimensionValues`: 2+ SQS queue URLs. The policy uses CloudWatch
|
|
107
|
+
* metric math to track the SUM of their depths under one signal — useful
|
|
108
|
+
* when several queues feed the same worker pool and the desired pod count
|
|
109
|
+
* must reflect total pending work, not the max of any one queue. (AWS's
|
|
110
|
+
* default with N parallel target-tracking policies is max-of-all, which
|
|
111
|
+
* undercounts when multiple queues have load simultaneously.)
|
|
112
|
+
*
|
|
113
|
+
* Setting both fields, or neither, throws at apply time.
|
|
114
|
+
*/
|
|
115
|
+
export type AppAutoscalingPolicy = {
|
|
116
|
+
metricName: string;
|
|
117
|
+
targetValue: number;
|
|
118
|
+
/** Single-queue form. Mutually exclusive with `metricDimensionValues`. */
|
|
119
|
+
metricDimensionValue?: pulumi.Output<string>;
|
|
120
|
+
/**
|
|
121
|
+
* Multi-queue (sum) form. Mutually exclusive with `metricDimensionValue`.
|
|
122
|
+
* Must contain ≥1 entry; with a single entry, prefer `metricDimensionValue`
|
|
123
|
+
* for clarity.
|
|
124
|
+
*/
|
|
125
|
+
metricDimensionValues?: pulumi.Output<string>[];
|
|
126
|
+
statistic: "Average" | "Minimum" | "Maximum";
|
|
127
|
+
scaleOutCooldown: number;
|
|
128
|
+
scaleInCooldown: number;
|
|
129
|
+
};
|
|
87
130
|
/**
|
|
88
131
|
*
|
|
89
132
|
* @param serviceName A name for this service. For example, "builder-api"
|
|
@@ -137,14 +180,7 @@ export type InternalServiceOptions = {
|
|
|
137
180
|
appAutoscaling?: {
|
|
138
181
|
maxCapacity: number;
|
|
139
182
|
minCapacity?: number;
|
|
140
|
-
policy?:
|
|
141
|
-
metricName: string;
|
|
142
|
-
targetValue: number;
|
|
143
|
-
metricDimensionValue: pulumi.Output<string>;
|
|
144
|
-
statistic: "Average" | "Minimum" | "Maximum";
|
|
145
|
-
scaleOutCooldown: number;
|
|
146
|
-
scaleInCooldown: number;
|
|
147
|
-
}[];
|
|
183
|
+
policy?: AppAutoscalingPolicy[];
|
|
148
184
|
scheduleAction?: {
|
|
149
185
|
maxCapacity: number;
|
|
150
186
|
minCapacity: number;
|
package/createFargateTask.js
CHANGED
|
@@ -49,12 +49,18 @@ async function getClusterInstance(cluster) {
|
|
|
49
49
|
return cluster.arn;
|
|
50
50
|
}
|
|
51
51
|
exports.getClusterInstance = getClusterInstance;
|
|
52
|
-
function getFargateExecutionRole(name, policyArnNamedMap) {
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
52
|
+
function getFargateExecutionRole(name, policyArnNamedMap, existingRole) {
|
|
53
|
+
let role;
|
|
54
|
+
if (existingRole) {
|
|
55
|
+
role = existingRole;
|
|
56
|
+
}
|
|
57
|
+
else {
|
|
58
|
+
const assumeRolePolicy = aws.iam.assumeRolePolicyForPrincipal({
|
|
59
|
+
Service: "ecs-tasks.amazonaws.com",
|
|
60
|
+
});
|
|
61
|
+
const dependsOn = Object.values(policyArnNamedMap).filter(($) => $ instanceof pulumi.Resource);
|
|
62
|
+
role = new aws.iam.Role(name, { assumeRolePolicy }, { dependsOn });
|
|
63
|
+
}
|
|
58
64
|
const policies = [];
|
|
59
65
|
// Default execution policy
|
|
60
66
|
policies.push(new aws.iam.RolePolicyAttachment(`${name}-default-execution-policy`, {
|
|
@@ -72,12 +78,18 @@ function getFargateExecutionRole(name, policyArnNamedMap) {
|
|
|
72
78
|
return { role, policies };
|
|
73
79
|
}
|
|
74
80
|
exports.getFargateExecutionRole = getFargateExecutionRole;
|
|
75
|
-
function getFargateTaskRole(name, policyArnNamedMap) {
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
+
function getFargateTaskRole(name, policyArnNamedMap, existingRole) {
|
|
82
|
+
let role;
|
|
83
|
+
if (existingRole) {
|
|
84
|
+
role = existingRole;
|
|
85
|
+
}
|
|
86
|
+
else {
|
|
87
|
+
const assumeRolePolicy = aws.iam.assumeRolePolicyForPrincipal({
|
|
88
|
+
Service: "ecs-tasks.amazonaws.com",
|
|
89
|
+
});
|
|
90
|
+
const dependsOn = Object.values(policyArnNamedMap).filter(($) => $ instanceof pulumi.Resource);
|
|
91
|
+
role = new aws.iam.Role(name, { assumeRolePolicy }, { dependsOn });
|
|
92
|
+
}
|
|
81
93
|
const policies = [];
|
|
82
94
|
Object.entries(policyArnNamedMap).forEach(([key, policyArn]) => {
|
|
83
95
|
if (policyArn instanceof aws.iam.Policy) {
|
|
@@ -104,7 +116,7 @@ exports.getFargateTaskRole = getFargateTaskRole;
|
|
|
104
116
|
* @param options.appAutoscaling Configuration for autoscaling
|
|
105
117
|
*/
|
|
106
118
|
async function createFargateTask(serviceName, dockerImage, dockerListeningPort, environment, hostname, options) {
|
|
107
|
-
let { healthCheck, healthCheckContainer, essential, dontExpose, securityGroups, cluster, memoryReservation, command, version, ephemeralStorageInGB, desiredCount, cpuReservation, extraPortMappings, extraALBMappings, nlbMappings, executionRolePolicies, taskRolePolicies, ignoreServiceDiscovery, secrets, metrics, forceNewDeployment, dontAssignPublicIp, dependsOn, volumes, deregistrationDelay, mountPoints, repositoryCredentials, team, appAutoscaling, enableExecuteCommand, } = options;
|
|
119
|
+
let { healthCheck, healthCheckContainer, essential, dontExpose, securityGroups, cluster, memoryReservation, command, version, ephemeralStorageInGB, desiredCount, cpuReservation, extraPortMappings, extraALBMappings, nlbMappings, executionRolePolicies, taskRolePolicies, taskRole: existingTaskRole, executionRole: existingExecutionRole, ignoreServiceDiscovery, secrets, metrics, forceNewDeployment, dontAssignPublicIp, dependsOn, volumes, deregistrationDelay, mountPoints, repositoryCredentials, team, appAutoscaling, enableExecuteCommand, } = options;
|
|
108
120
|
if (undefined === essential) {
|
|
109
121
|
essential = true;
|
|
110
122
|
}
|
|
@@ -141,9 +153,9 @@ async function createFargateTask(serviceName, dockerImage, dockerListeningPort,
|
|
|
141
153
|
if (undefined === secrets) {
|
|
142
154
|
secrets = [];
|
|
143
155
|
}
|
|
144
|
-
const { role: executionRole, policies: executionPolicies } = getFargateExecutionRole(`${serviceName}-${version}-execution`, executionRolePolicies || {});
|
|
156
|
+
const { role: executionRole, policies: executionPolicies } = getFargateExecutionRole(`${serviceName}-${version}-execution`, executionRolePolicies || {}, existingExecutionRole);
|
|
145
157
|
dependsOn.push(...executionPolicies);
|
|
146
|
-
const { role: taskRole, policies } = getFargateTaskRole(`${serviceName}-${version}-task`, taskRolePolicies || {});
|
|
158
|
+
const { role: taskRole, policies } = getFargateTaskRole(`${serviceName}-${version}-task`, taskRolePolicies || {}, existingTaskRole);
|
|
147
159
|
dependsOn.push(...policies);
|
|
148
160
|
let dockerLabels = {};
|
|
149
161
|
if (metrics && (metrics.port || dockerListeningPort)) {
|
|
@@ -418,26 +430,78 @@ function setAutoscaling(service, serviceName, config) {
|
|
|
418
430
|
scalableDimension: "ecs:service:DesiredCount",
|
|
419
431
|
serviceNamespace: "ecs",
|
|
420
432
|
});
|
|
433
|
+
// The dcl-ops-lib SQS queue convention is to pass `queue.id` (the queue's
|
|
434
|
+
// URL, e.g., https://sqs.us-east-1.amazonaws.com/.../my-queue) as the
|
|
435
|
+
// metric dimension value. CloudWatch wants just the queue NAME ("my-queue")
|
|
436
|
+
// in the dimension, so we trim everything up to and including the last `/`.
|
|
437
|
+
const extractQueueName = (queueUrl) => queueUrl.apply((value) => value.split("/").pop());
|
|
421
438
|
if (config.appAutoscaling.policy) {
|
|
422
439
|
config.appAutoscaling.policy.forEach((item, index) => {
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
]
|
|
434
|
-
|
|
435
|
-
unit: "Count",
|
|
436
|
-
};
|
|
440
|
+
// Validate the singular/plural choice. Done per-item so the user sees
|
|
441
|
+
// the failure on the offending policy entry rather than a vague top-
|
|
442
|
+
// level error after Pulumi has already started planning resources.
|
|
443
|
+
const hasSingle = item.metricDimensionValue !== undefined;
|
|
444
|
+
const hasMulti = item.metricDimensionValues !== undefined &&
|
|
445
|
+
item.metricDimensionValues.length > 0;
|
|
446
|
+
if (hasSingle && hasMulti) {
|
|
447
|
+
throw new Error(`appAutoscaling.policy[${index}] sets both metricDimensionValue and metricDimensionValues; pick one.`);
|
|
448
|
+
}
|
|
449
|
+
if (!hasSingle && !hasMulti) {
|
|
450
|
+
throw new Error(`appAutoscaling.policy[${index}] must set either metricDimensionValue or metricDimensionValues (non-empty).`);
|
|
451
|
+
}
|
|
437
452
|
let TTS_CustomizedMetricSpecification = undefined;
|
|
438
453
|
if (item.metricName === "ApproximateNumberOfMessagesVisible") {
|
|
439
|
-
|
|
440
|
-
|
|
454
|
+
if (hasMulti) {
|
|
455
|
+
// Metric-math form: SUM the queue depths into one signal. Each
|
|
456
|
+
// queue contributes a `mN` metric stat with returnData=false, and
|
|
457
|
+
// a final `e0` expression `m0 + m1 + ...` is the value the scaling
|
|
458
|
+
// target tracks. AWS evaluates the expression at policy time.
|
|
459
|
+
const dims = item.metricDimensionValues;
|
|
460
|
+
const metricStats = dims.map((queueUrl, i) => ({
|
|
461
|
+
id: `m${i}`,
|
|
462
|
+
metricStat: {
|
|
463
|
+
metric: {
|
|
464
|
+
metricName: item.metricName,
|
|
465
|
+
namespace: "AWS/SQS",
|
|
466
|
+
dimensions: [
|
|
467
|
+
{
|
|
468
|
+
name: "QueueName",
|
|
469
|
+
value: extractQueueName(queueUrl),
|
|
470
|
+
},
|
|
471
|
+
],
|
|
472
|
+
},
|
|
473
|
+
stat: item.statistic,
|
|
474
|
+
unit: "Count",
|
|
475
|
+
},
|
|
476
|
+
returnData: false,
|
|
477
|
+
}));
|
|
478
|
+
const expression = dims.map((_, i) => `m${i}`).join(" + ");
|
|
479
|
+
TTS_CustomizedMetricSpecification = {
|
|
480
|
+
metrics: [
|
|
481
|
+
...metricStats,
|
|
482
|
+
{
|
|
483
|
+
id: "e0",
|
|
484
|
+
expression,
|
|
485
|
+
returnData: true,
|
|
486
|
+
},
|
|
487
|
+
],
|
|
488
|
+
};
|
|
489
|
+
}
|
|
490
|
+
else {
|
|
491
|
+
// Single-queue form (existing behavior, byte-identical output).
|
|
492
|
+
TTS_CustomizedMetricSpecification = {
|
|
493
|
+
metricName: item.metricName,
|
|
494
|
+
namespace: "AWS/SQS",
|
|
495
|
+
dimensions: [
|
|
496
|
+
{
|
|
497
|
+
name: "QueueName",
|
|
498
|
+
value: extractQueueName(item.metricDimensionValue),
|
|
499
|
+
},
|
|
500
|
+
],
|
|
501
|
+
statistic: item.statistic,
|
|
502
|
+
unit: "Count",
|
|
503
|
+
};
|
|
504
|
+
}
|
|
441
505
|
}
|
|
442
506
|
return new aws.appautoscaling.Policy(`ecs-autoscaling-policy-${(0, stack_1.getStackScopedName)(serviceName)}-${index.toString()}`, // distinct policies by id
|
|
443
507
|
{
|