Skip to content

Commit

Permalink
feat(events-targets): allow all ECS TaskOverrides (#32344)
Browse files Browse the repository at this point in the history
### Issue #32217

Closes #32217.

### Reason for this change

ECS targets can override any item in the [`TaskOverride` structure](https://docs.aws.amazon.com/AmazonECS/latest/APIReference/API_TaskOverride.html) via the `input` parameter, according to [the docs](https://docs.aws.amazon.com/eventbridge/latest/userguide/eb-targets.html#targets-specifics-ecs-task).

However, today, only the `containerOverrides` option is exposed: https://github.com/aws/aws-cdk/blob/1b7265bf5cc623b2e362266c96ce866df539581f/packages/aws-cdk-lib/aws-events-targets/lib/ecs-task.ts#L225-L227

### Description of changes

This PR adds all `TaskOverride` properties to the `input` parameter.

### Description of how you validated changes

I add unit tests. I also updated the existing integration tests to include additional overrides. I validated them in my AWS account.

### Checklist
- [x] My code adheres to the [CONTRIBUTING GUIDE](https://github.com/aws/aws-cdk/blob/main/CONTRIBUTING.md) and [DESIGN GUIDELINES](https://github.com/aws/aws-cdk/blob/main/docs/DESIGN_GUIDELINES.md)

----

*By submitting this pull request, I confirm that my contribution is made under the terms of the Apache-2.0 license*
  • Loading branch information
blimmer authored Feb 12, 2025
1 parent a0fa042 commit aebb331
Show file tree
Hide file tree
Showing 20 changed files with 286 additions and 31 deletions.

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Original file line number Diff line number Diff line change
Expand Up @@ -1028,7 +1028,7 @@
}
},
"Id": "Target0",
"Input": "{\"containerOverrides\":[{\"name\":\"TheContainer\",\"environment\":[{\"name\":\"I_WAS_TRIGGERED\",\"value\":\"From CloudWatch Events\"}]}]}",
"Input": "{\"containerOverrides\":[{\"name\":\"TheContainer\",\"environment\":[{\"name\":\"I_WAS_TRIGGERED\",\"value\":\"From CloudWatch Events\"}]}],\"cpu\":\"512\",\"memory\":\"512\"}",
"RoleArn": {
"Fn::GetAtt": [
"TaskDefEventsRoleFB3B67B8",
Expand Down Expand Up @@ -1078,4 +1078,4 @@
]
}
}
}
}

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,8 @@ rule.addTarget(new targets.EcsTask({
cluster,
taskDefinition,
taskCount: 1,
cpu: '512',
memory: '512',
containerOverrides: [{
containerName: 'TheContainer',
environment: [
Expand Down

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Original file line number Diff line number Diff line change
Expand Up @@ -831,7 +831,7 @@
}
},
"Id": "Target0",
"Input": "{\"containerOverrides\":[{\"name\":\"TheContainer\",\"environment\":[{\"name\":\"I_WAS_TRIGGERED\",\"value\":\"From CloudWatch Events\"}]}]}",
"Input": "{\"containerOverrides\":[{\"name\":\"TheContainer\",\"environment\":[{\"name\":\"I_WAS_TRIGGERED\",\"value\":\"From CloudWatch Events\"}]}],\"cpu\":\"512\",\"memory\":\"512\"}",
"RoleArn": {
"Fn::GetAtt": [
"TaskDefEventsRoleFB3B67B8",
Expand Down Expand Up @@ -959,4 +959,4 @@
]
}
}
}
}

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,8 @@ rule.addTarget(new targets.EcsTask({
taskDefinition,
taskCount: 1,
enableExecuteCommand: true,
cpu: '512',
memory: '512',
containerOverrides: [{
containerName: 'TheContainer',
environment: [
Expand Down
27 changes: 27 additions & 0 deletions packages/aws-cdk-lib/aws-events-targets/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -564,6 +564,33 @@ rule.addTarget(new targets.EcsTask({
}));
```

### Overriding Values in the Task Definition

You can override values in the task definition by setting the corresponding properties in the `EcsTaskProps`. All
values in the [`TaskOverrides` API](https://docs.aws.amazon.com/AmazonECS/latest/APIReference/API_TaskOverride.html) are
supported.

```ts
import * as ecs from 'aws-cdk-lib/aws-ecs';

declare const cluster: ecs.ICluster;
declare const taskDefinition: ecs.TaskDefinition;

const rule = new events.Rule(this, 'Rule', {
schedule: events.Schedule.rate(cdk.Duration.hours(1)),
});

rule.addTarget(new targets.EcsTask({
cluster,
taskDefinition,
taskCount: 1,

// Overrides the cpu and memory values in the task definition
cpu: '512',
memory: '512',
}));
```

## Schedule a Redshift query (serverless or cluster)

Use the `RedshiftQuery` target to schedule an Amazon Redshift Query.
Expand Down
28 changes: 28 additions & 0 deletions packages/aws-cdk-lib/aws-events-targets/lib/ecs-task-properties.ts
Original file line number Diff line number Diff line change
Expand Up @@ -56,3 +56,31 @@ export interface TaskEnvironmentVariable {
*/
readonly value: string;
}

/**
* Override ephemeral storage for the task.
*/
export interface EphemeralStorageOverride {
/**
* The total amount, in GiB, of ephemeral storage to set for the task.
*
* The minimum supported value is 20 GiB and the maximum supported value is 200 GiB.
*/
readonly sizeInGiB: number;
}

/**
* Override inference accelerators for the task.
*/
export interface InferenceAcceleratorOverride {
/**
* The Elastic Inference accelerator device name to override for the task.
* This parameter must match a `deviceName` specified in the task definition.
*/
readonly deviceName: string;

/**
* The Elastic Inference accelerator type to use.
*/
readonly deviceType: string;
}
79 changes: 75 additions & 4 deletions packages/aws-cdk-lib/aws-events-targets/lib/ecs-task.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { Construct } from 'constructs';
import { ContainerOverride } from './ecs-task-properties';
import { ContainerOverride, EphemeralStorageOverride, InferenceAcceleratorOverride } from './ecs-task-properties';
import { addToDeadLetterQueueResourcePolicy, bindBaseTargetConfig, singletonEventRole, TargetBaseProps } from './util';
import * as ec2 from '../../aws-ec2';
import * as ecs from '../../aws-ecs';
Expand Down Expand Up @@ -50,6 +50,54 @@ export interface EcsTaskProps extends TargetBaseProps {
*/
readonly containerOverrides?: ContainerOverride[];

/**
* The CPU override for the task.
*
* @default - The task definition's CPU value
*/
readonly cpu?: string;

/**
* The ephemeral storage setting override for the task.
*
* NOTE: This parameter is only supported for tasks hosted on Fargate that use the following platform versions:
* - Linux platform version 1.4.0 or later.
* - Windows platform version 1.0.0 or later.
*
* @default - The task definition's ephemeral storage value
*/
readonly ephemeralStorage?: EphemeralStorageOverride;

/**
* The execution role for the task.
*
* The Amazon Resource Name (ARN) of the task execution role override for the task.
*
* @default - The task definition's execution role
*/
readonly executionRole?: iam.IRole;

/**
* The Elastic Inference accelerator override for the task.
*
* @default - The task definition's inference accelerator overrides
*/
readonly inferenceAcceleratorOverrides?: InferenceAcceleratorOverride[];

/**
* The memory override for the task.
*
* @default - The task definition's memory value
*/
readonly memory?: string;

/**
* The IAM role for the task.
*
* @default - The task definition's task role
*/
readonly taskRole?: iam.IRole;

/**
* In what subnets to place the task's ENIs
*
Expand Down Expand Up @@ -222,14 +270,12 @@ export class EcsTask implements events.IRuleTarget {
public bind(_rule: events.IRule, _id?: string): events.RuleTargetConfig {
const arn = this.cluster.clusterArn;
const role = this.role;
const containerOverrides = this.props.containerOverrides && this.props.containerOverrides
.map(({ containerName, ...overrides }) => ({ name: containerName, ...overrides }));
const input = { containerOverrides };
const taskCount = this.taskCount;
const taskDefinitionArn = this.taskDefinition.taskDefinitionArn;
const propagateTags = this.propagateTags;
const tagList = this.tags;
const enableExecuteCommand = this.enableExecuteCommand;
const input = this.createInput();

const subnetSelection = this.props.subnetSelection || { subnetType: ec2.SubnetType.PRIVATE_WITH_EGRESS };

Expand Down Expand Up @@ -276,6 +322,31 @@ export class EcsTask implements events.IRuleTarget {
};
}

private createInput(): Record<string, any> {
const containerOverrides = this.props.containerOverrides && this.props.containerOverrides
.map(({ containerName, ...overrides }) => ({ name: containerName, ...overrides }));

if (this.props.ephemeralStorage) {
const ephemeralStorage = this.props.ephemeralStorage;
if (ephemeralStorage.sizeInGiB < 20 || ephemeralStorage.sizeInGiB > 200) {
throw new Error('Ephemeral storage size must be between 20 GiB and 200 GiB.');
}
}

// See https://docs.aws.amazon.com/AmazonECS/latest/APIReference/API_TaskOverride.html
return {
// In prior versions, containerOverrides was passed even when undefined, so we always set it for backward compatibility.
containerOverrides,

...(this.props.cpu && { cpu: this.props.cpu }),
...(this.props.ephemeralStorage && { ephemeralStorage: this.props.ephemeralStorage }),
...(this.props.executionRole?.roleArn && { executionRole: this.props.executionRole.roleArn }),
...(this.props.inferenceAcceleratorOverrides && { inferenceAcceleratorOverrides: this.props.inferenceAcceleratorOverrides }),
...(this.props.memory && { memory: this.props.memory }),
...(this.props.taskRole?.roleArn && { taskRole: this.props.taskRole.roleArn }),
};
}

private createEventRolePolicyStatements(): iam.PolicyStatement[] {
// check if there is a taskdefinition revision (arn will end with : followed by digits) included in the arn already
let needsRevisionWildcard = false;
Expand Down
Loading

0 comments on commit aebb331

Please sign in to comment.