in src/constructs/ecs/ecs-task.ts [170:314]
constructor(scope: GuStack, id: string, props: GuEcsTaskProps) {
super(scope, id);
const {
app,
// see https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-ecs-taskdefinition.html#cfn-ecs-taskdefinition-cpu for details
cpu = 2048, // 2 cores and from 4-16GB memory
memory = 4096, // 4GB
storage, // default is 20 GB when not provided in props
containerConfiguration,
taskCommand,
taskTimeoutInMinutes = 15,
customTaskPolicies,
vpc,
subnets,
monitoringConfiguration,
securityGroups = [],
environmentOverrides,
enableDistributablePolicy = true,
containerInsights,
} = props;
if (storage && storage < 21) {
throw new Error(
"Storage must be at least 21. See https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-ecs-taskdefinition-ephemeralstorage.html",
);
}
const { stack, stage } = scope;
const cluster = new Cluster(scope, `${id}-Cluster`, {
clusterName: `${app}-cluster-${stage}`,
enableFargateCapacityProviders: true,
vpc,
containerInsightsV2: containerInsights,
});
const taskDefinition = new TaskDefinition(scope, `${id}-TaskDefinition`, {
compatibility: Compatibility.FARGATE,
cpu: cpu.toString(),
memoryMiB: memory.toString(),
family: `${stack}-${stage}-${app}`,
ephemeralStorageGiB: storage,
runtimePlatform: {
cpuArchitecture: CpuArchitecture.ARM64,
operatingSystemFamily: OperatingSystemFamily.of("LINUX"),
},
});
this.taskDefinition = taskDefinition;
const containerDefinition = taskDefinition.addContainer(`${id}-TaskContainer`, {
image: getContainer(containerConfiguration),
entryPoint: taskCommand ? ["/bin/sh"] : undefined,
command: taskCommand ? ["-c", taskCommand] : undefined, // if unset, falls back to CMD in docker file, or no command will be run
cpu,
memoryLimitMiB: memory,
logging: LogDrivers.awsLogs({
streamPrefix: app,
logRetention: 14,
}),
readonlyRootFilesystem: true,
});
this.containerDefinition = containerDefinition;
if (enableDistributablePolicy) {
const distPolicy = new GuGetDistributablePolicyStatement(scope, { app });
taskDefinition.addToTaskRolePolicy(distPolicy);
}
(customTaskPolicies ?? []).forEach((p) => taskDefinition.addToTaskRolePolicy(p));
const task = new EcsRunTask(scope, `${id}-task`, {
cluster,
launchTarget: new EcsFargateLaunchTarget({
platformVersion: FargatePlatformVersion.LATEST,
}),
taskDefinition,
subnets: { subnets },
assignPublicIp: false,
integrationPattern: IntegrationPattern.RUN_JOB,
resultPath: JsonPath.DISCARD,
taskTimeout: Timeout.duration(Duration.minutes(taskTimeoutInMinutes)),
securityGroups,
containerOverrides: [
{
containerDefinition: containerDefinition,
environment: environmentOverrides,
},
],
});
this.task = task;
this.stateMachine = new StateMachine(scope, `${id}-StateMachine`, {
definitionBody: DefinitionBody.fromChainable(task),
stateMachineName: `${app}-${stage}`,
});
if (!monitoringConfiguration.noMonitoring) {
const alarmTopic = Topic.fromTopicArn(
scope,
AppIdentity.suffixText(props, "AlarmTopic"),
monitoringConfiguration.snsTopicArn,
);
const alarms = [
{
name: `${app}-execution-failed`,
description: `${app}-${stage} job failed `,
metric: this.stateMachine.metricFailed({
period: Duration.hours(1),
statistic: "sum",
}),
},
{
name: `${app}-timeout`,
description: `${app}-${stage} job timed out `,
metric: this.stateMachine.metricTimedOut({
period: Duration.hours(1),
statistic: "sum",
}),
},
];
alarms.forEach(({ name, description, metric }) => {
const alarm = new Alarm(scope, name, {
alarmDescription: description,
actionsEnabled: true,
metric: metric,
// default for comparisonOperator is GreaterThanOrEqualToThreshold
threshold: 1,
evaluationPeriods: 1,
treatMissingData: TreatMissingData.NOT_BREACHING,
});
alarm.addAlarmAction(new SnsAction(alarmTopic));
AppIdentity.taggedConstruct({ app }, alarm);
});
}
// Tag all constructs with correct app tag
[cluster, task, taskDefinition, this.stateMachine].forEach((c) => AppIdentity.taggedConstruct({ app }, c));
new CfnOutput(scope, `${id}-StateMachineArnOutput`, {
value: this.stateMachine.stateMachineArn,
});
}