constructor()

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,
    });
  }