From 986e378a27b19271695f735ad01603a4c1386d25 Mon Sep 17 00:00:00 2001 From: kacky Date: Fri, 23 Aug 2024 10:49:27 +0900 Subject: [PATCH] feat(stepfunctions-tasks): add cpu and memory parameters to EcsRunTask (#30140) ### Issue # (if applicable) Closes #30027 . ### Reason for this change As described in the issue. ### Description of changes Add cpu and memoryMiB property to EcsRunTaskProps. ### Description of how you validated changes Add unit tests. ### 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* --- ...sfn-tasks-ecs-fargate-run-task.assets.json | 4 +- ...n-tasks-ecs-fargate-run-task.template.json | 2 +- .../manifest.json | 2 +- .../tree.json | 8 +-- .../test/ecs/integ.fargate-run-task.ts | 2 + .../aws-stepfunctions-tasks/README.md | 33 +++++++++ .../lib/ecs/run-task.ts | 67 ++++++++++++++----- .../test/ecs/run-tasks.test.ts | 54 +++++++++++++++ 8 files changed, 147 insertions(+), 25 deletions(-) diff --git a/packages/@aws-cdk-testing/framework-integ/test/aws-stepfunctions-tasks/test/ecs/integ.fargate-run-task.js.snapshot/aws-sfn-tasks-ecs-fargate-run-task.assets.json b/packages/@aws-cdk-testing/framework-integ/test/aws-stepfunctions-tasks/test/ecs/integ.fargate-run-task.js.snapshot/aws-sfn-tasks-ecs-fargate-run-task.assets.json index 7ecf04e0d81ff..f021a4b28445e 100644 --- a/packages/@aws-cdk-testing/framework-integ/test/aws-stepfunctions-tasks/test/ecs/integ.fargate-run-task.js.snapshot/aws-sfn-tasks-ecs-fargate-run-task.assets.json +++ b/packages/@aws-cdk-testing/framework-integ/test/aws-stepfunctions-tasks/test/ecs/integ.fargate-run-task.js.snapshot/aws-sfn-tasks-ecs-fargate-run-task.assets.json @@ -1,7 +1,7 @@ { "version": "36.0.0", "files": { - "c53e190df9c2b45bab08c41d91ec855dce842f2caccb0d3a17a8304f6fe53059": { + "13e6306137d736d171820c0b39be97e381b7bc53c9d9dcb76d742223d557d4a1": { "source": { "path": "aws-sfn-tasks-ecs-fargate-run-task.template.json", "packaging": "file" @@ -9,7 +9,7 @@ "destinations": { "current_account-current_region": { "bucketName": "cdk-hnb659fds-assets-${AWS::AccountId}-${AWS::Region}", - "objectKey": "c53e190df9c2b45bab08c41d91ec855dce842f2caccb0d3a17a8304f6fe53059.json", + "objectKey": "13e6306137d736d171820c0b39be97e381b7bc53c9d9dcb76d742223d557d4a1.json", "assumeRoleArn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-file-publishing-role-${AWS::AccountId}-${AWS::Region}" } } diff --git a/packages/@aws-cdk-testing/framework-integ/test/aws-stepfunctions-tasks/test/ecs/integ.fargate-run-task.js.snapshot/aws-sfn-tasks-ecs-fargate-run-task.template.json b/packages/@aws-cdk-testing/framework-integ/test/aws-stepfunctions-tasks/test/ecs/integ.fargate-run-task.js.snapshot/aws-sfn-tasks-ecs-fargate-run-task.template.json index bcb8e3e62506c..a9987bc5c6308 100644 --- a/packages/@aws-cdk-testing/framework-integ/test/aws-stepfunctions-tasks/test/ecs/integ.fargate-run-task.js.snapshot/aws-sfn-tasks-ecs-fargate-run-task.template.json +++ b/packages/@aws-cdk-testing/framework-integ/test/aws-stepfunctions-tasks/test/ecs/integ.fargate-run-task.js.snapshot/aws-sfn-tasks-ecs-fargate-run-task.template.json @@ -828,7 +828,7 @@ "GroupId" ] }, - "\"]}},\"Overrides\":{\"ContainerOverrides\":[{\"Name\":\"TheContainer\",\"Environment\":[{\"Name\":\"SOME_KEY\",\"Value.$\":\"$.SomeKey\"}]}]},\"LaunchType\":\"FARGATE\",\"PlatformVersion\":\"1.4.0\"}},\"FargateTaskSetRevisionNumber\":{\"Next\":\"FargateTaskWithPropagatedTag\",\"Type\":\"Task\",\"Resource\":\"arn:", + "\"]}},\"Overrides\":{\"Cpu\":\"1024\",\"Memory\":\"2048\",\"ContainerOverrides\":[{\"Name\":\"TheContainer\",\"Environment\":[{\"Name\":\"SOME_KEY\",\"Value.$\":\"$.SomeKey\"}]}]},\"LaunchType\":\"FARGATE\",\"PlatformVersion\":\"1.4.0\"}},\"FargateTaskSetRevisionNumber\":{\"Next\":\"FargateTaskWithPropagatedTag\",\"Type\":\"Task\",\"Resource\":\"arn:", { "Ref": "AWS::Partition" }, diff --git a/packages/@aws-cdk-testing/framework-integ/test/aws-stepfunctions-tasks/test/ecs/integ.fargate-run-task.js.snapshot/manifest.json b/packages/@aws-cdk-testing/framework-integ/test/aws-stepfunctions-tasks/test/ecs/integ.fargate-run-task.js.snapshot/manifest.json index ba001677be308..6bce983fef74b 100644 --- a/packages/@aws-cdk-testing/framework-integ/test/aws-stepfunctions-tasks/test/ecs/integ.fargate-run-task.js.snapshot/manifest.json +++ b/packages/@aws-cdk-testing/framework-integ/test/aws-stepfunctions-tasks/test/ecs/integ.fargate-run-task.js.snapshot/manifest.json @@ -18,7 +18,7 @@ "validateOnSynth": false, "assumeRoleArn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-deploy-role-${AWS::AccountId}-${AWS::Region}", "cloudFormationExecutionRoleArn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-cfn-exec-role-${AWS::AccountId}-${AWS::Region}", - "stackTemplateAssetObjectUrl": "s3://cdk-hnb659fds-assets-${AWS::AccountId}-${AWS::Region}/c53e190df9c2b45bab08c41d91ec855dce842f2caccb0d3a17a8304f6fe53059.json", + "stackTemplateAssetObjectUrl": "s3://cdk-hnb659fds-assets-${AWS::AccountId}-${AWS::Region}/13e6306137d736d171820c0b39be97e381b7bc53c9d9dcb76d742223d557d4a1.json", "requiresBootstrapStackVersion": 6, "bootstrapStackVersionSsmParameter": "/cdk-bootstrap/hnb659fds/version", "additionalDependencies": [ diff --git a/packages/@aws-cdk-testing/framework-integ/test/aws-stepfunctions-tasks/test/ecs/integ.fargate-run-task.js.snapshot/tree.json b/packages/@aws-cdk-testing/framework-integ/test/aws-stepfunctions-tasks/test/ecs/integ.fargate-run-task.js.snapshot/tree.json index 6a5da72d4e382..27044764c7bee 100644 --- a/packages/@aws-cdk-testing/framework-integ/test/aws-stepfunctions-tasks/test/ecs/integ.fargate-run-task.js.snapshot/tree.json +++ b/packages/@aws-cdk-testing/framework-integ/test/aws-stepfunctions-tasks/test/ecs/integ.fargate-run-task.js.snapshot/tree.json @@ -1012,7 +1012,7 @@ } }, "constructInfo": { - "fqn": "aws-cdk-lib.aws_stepfunctions.TaskStateBase", + "fqn": "aws-cdk-lib.aws_stepfunctions_tasks.EcsRunTask", "version": "0.0.0" } }, @@ -1056,7 +1056,7 @@ } }, "constructInfo": { - "fqn": "aws-cdk-lib.aws_stepfunctions.TaskStateBase", + "fqn": "aws-cdk-lib.aws_stepfunctions_tasks.EcsRunTask", "version": "0.0.0" } }, @@ -1100,7 +1100,7 @@ } }, "constructInfo": { - "fqn": "aws-cdk-lib.aws_stepfunctions.TaskStateBase", + "fqn": "aws-cdk-lib.aws_stepfunctions_tasks.EcsRunTask", "version": "0.0.0" } }, @@ -1391,7 +1391,7 @@ "GroupId" ] }, - "\"]}},\"Overrides\":{\"ContainerOverrides\":[{\"Name\":\"TheContainer\",\"Environment\":[{\"Name\":\"SOME_KEY\",\"Value.$\":\"$.SomeKey\"}]}]},\"LaunchType\":\"FARGATE\",\"PlatformVersion\":\"1.4.0\"}},\"FargateTaskSetRevisionNumber\":{\"Next\":\"FargateTaskWithPropagatedTag\",\"Type\":\"Task\",\"Resource\":\"arn:", + "\"]}},\"Overrides\":{\"Cpu\":\"1024\",\"Memory\":\"2048\",\"ContainerOverrides\":[{\"Name\":\"TheContainer\",\"Environment\":[{\"Name\":\"SOME_KEY\",\"Value.$\":\"$.SomeKey\"}]}]},\"LaunchType\":\"FARGATE\",\"PlatformVersion\":\"1.4.0\"}},\"FargateTaskSetRevisionNumber\":{\"Next\":\"FargateTaskWithPropagatedTag\",\"Type\":\"Task\",\"Resource\":\"arn:", { "Ref": "AWS::Partition" }, diff --git a/packages/@aws-cdk-testing/framework-integ/test/aws-stepfunctions-tasks/test/ecs/integ.fargate-run-task.ts b/packages/@aws-cdk-testing/framework-integ/test/aws-stepfunctions-tasks/test/ecs/integ.fargate-run-task.ts index 3dad45d7604a3..8d5ff62afe5e9 100644 --- a/packages/@aws-cdk-testing/framework-integ/test/aws-stepfunctions-tasks/test/ecs/integ.fargate-run-task.ts +++ b/packages/@aws-cdk-testing/framework-integ/test/aws-stepfunctions-tasks/test/ecs/integ.fargate-run-task.ts @@ -57,6 +57,8 @@ const definition = new sfn.Pass(stack, 'Start', { platformVersion: ecs.FargatePlatformVersion.VERSION1_4, }), taskTimeout: sfn.Timeout.at('$.Timeout'), + cpu: '1024', + memoryMiB: '2048', }), ).next( new tasks.EcsRunTask(stack, 'FargateTaskSetRevisionNumber', { diff --git a/packages/aws-cdk-lib/aws-stepfunctions-tasks/README.md b/packages/aws-cdk-lib/aws-stepfunctions-tasks/README.md index 9f5943afaa16b..a8175c94cc8ff 100644 --- a/packages/aws-cdk-lib/aws-stepfunctions-tasks/README.md +++ b/packages/aws-cdk-lib/aws-stepfunctions-tasks/README.md @@ -701,6 +701,39 @@ const runTask = new tasks.EcsRunTask(this, 'RunFargate', { }); ``` +#### Override CPU and Memory Parameter + +By setting the property cpu or memoryMiB, you can override the Fargate or EC2 task instance size at runtime. + +see: https://docs.aws.amazon.com/AmazonECS/latest/APIReference/API_TaskOverride.html + +```ts +const vpc = ec2.Vpc.fromLookup(this, 'Vpc', { + isDefault: true, +}); +const cluster = new ecs.Cluster(this, 'ECSCluster', { vpc }); + +const taskDefinition = new ecs.TaskDefinition(this, 'TD', { + compatibility: ecs.Compatibility.FARGATE, + cpu: '256', + memoryMiB: '512' +}); + +taskDefinition.addContainer('TheContainer', { + image: ecs.ContainerImage.fromRegistry('foo/bar'), +}); + +const runTask = new tasks.EcsRunTask(this, 'Run', { + integrationPattern: sfn.IntegrationPattern.RUN_JOB, + cluster, + taskDefinition, + launchTarget: new tasks.EcsFargateLaunchTarget(), + cpu: '1024', + memoryMiB: '1048' +}); +``` + + #### ECS enable Exec By setting the property [`enableExecuteCommand`](https://docs.aws.amazon.com/AmazonECS/latest/APIReference/API_RunTask.html#ECS-RunTask-request-enableExecuteCommand) to `true`, you can enable the [ECS Exec feature](https://docs.aws.amazon.com/AmazonECS/latest/developerguide/ecs-exec.html) for the task for either Fargate or EC2 launch types. diff --git a/packages/aws-cdk-lib/aws-stepfunctions-tasks/lib/ecs/run-task.ts b/packages/aws-cdk-lib/aws-stepfunctions-tasks/lib/ecs/run-task.ts index 6ab4fc5cedbe7..caae28c6d891c 100644 --- a/packages/aws-cdk-lib/aws-stepfunctions-tasks/lib/ecs/run-task.ts +++ b/packages/aws-cdk-lib/aws-stepfunctions-tasks/lib/ecs/run-task.ts @@ -90,6 +90,20 @@ export interface EcsRunTaskProps extends sfn.TaskStateBaseProps { * @default false */ readonly enableExecuteCommand?: boolean; + + /** + * Cpu setting override + * @see https://docs.aws.amazon.com/AmazonECS/latest/APIReference/API_TaskOverride.html + * @default - No override + */ + readonly cpu?: string; + + /** + * Memory setting override + * @see https://docs.aws.amazon.com/AmazonECS/latest/APIReference/API_TaskOverride.html + * @default - No override + */ + readonly memoryMiB?: string; } /** @@ -310,7 +324,12 @@ export class EcsRunTask extends sfn.TaskStateBase implements ec2.IConnectable { Cluster: this.props.cluster.clusterArn, TaskDefinition: this.props.revisionNumber === undefined ? this.props.taskDefinition.family : `${this.props.taskDefinition.family}:${this.props.revisionNumber.toString()}`, NetworkConfiguration: this.networkConfiguration, - Overrides: renderOverrides(this.props.containerOverrides), + Overrides: renderOverrides( + { + cpu: this.props.cpu, + memoryMiB: this.props.memoryMiB, + containerOverrides: this.props.containerOverrides, + }), PropagateTags: this.props.propagatedTagSource, ...this.props.launchTarget.bind(this, { taskDefinition: this.props.taskDefinition, cluster: this.props.cluster }).parameters, EnableExecuteCommand: this.props.enableExecuteCommand, @@ -415,26 +434,40 @@ export class EcsRunTask extends sfn.TaskStateBase implements ec2.IConnectable { } } -function renderOverrides(containerOverrides?: ContainerOverride[]) { - if (!containerOverrides || containerOverrides.length === 0) { +interface OverrideProps { + cpu?: string; + memoryMiB?: string; + containerOverrides?: ContainerOverride[]; +} + +function renderOverrides(props: OverrideProps) { + const containerOverrides = props.containerOverrides; + const noContainerOverrides = !containerOverrides || containerOverrides.length === 0; + if (noContainerOverrides && !props.cpu && !props.memoryMiB) { return undefined; } const ret = new Array(); - for (const override of containerOverrides) { - ret.push({ - Name: override.containerDefinition.containerName, - Command: override.command, - Cpu: override.cpu, - Memory: override.memoryLimit, - MemoryReservation: override.memoryReservation, - Environment: - override.environment?.map((e) => ({ - Name: e.name, - Value: e.value, - })), - }); + if (!noContainerOverrides) { + for (const override of containerOverrides) { + ret.push({ + Name: override.containerDefinition.containerName, + Command: override.command, + Cpu: override.cpu, + Memory: override.memoryLimit, + MemoryReservation: override.memoryReservation, + Environment: + override.environment?.map((e) => ({ + Name: e.name, + Value: e.value, + })), + }); + } } - return { ContainerOverrides: ret }; + return { + Cpu: props.cpu, + Memory: props.memoryMiB, + ContainerOverrides: noContainerOverrides ? undefined : ret, + }; } diff --git a/packages/aws-cdk-lib/aws-stepfunctions-tasks/test/ecs/run-tasks.test.ts b/packages/aws-cdk-lib/aws-stepfunctions-tasks/test/ecs/run-tasks.test.ts index 9ef23e8e9a3d6..ff0e2c4f9eddb 100644 --- a/packages/aws-cdk-lib/aws-stepfunctions-tasks/test/ecs/run-tasks.test.ts +++ b/packages/aws-cdk-lib/aws-stepfunctions-tasks/test/ecs/run-tasks.test.ts @@ -209,6 +209,60 @@ test('Running a task with NONE as propagated tag source', () => { }).toStateJson())).toHaveProperty('Parameters.PropagateTags', 'NONE'); }); +test('Running a task with cpu parameter', () => { + const taskDefinition = new ecs.TaskDefinition(stack, 'TD', { + memoryMiB: '1024', + cpu: '512', + compatibility: ecs.Compatibility.FARGATE, + }); + const containerDefinition = taskDefinition.addContainer('TheContainer', { + containerName: 'ExplicitContainerName', + image: ecs.ContainerImage.fromRegistry('foo/bar'), + memoryLimitMiB: 256, + }); + + expect(stack.resolve( + new tasks.EcsRunTask(stack, 'task', { + cluster, + taskDefinition, + cpu: '1024', + containerOverrides: [ + { + containerDefinition, + environment: [{ name: 'SOME_KEY', value: sfn.JsonPath.stringAt('$.SomeKey') }], + }, + ], + launchTarget: new tasks.EcsFargateLaunchTarget(), + }).toStateJson())).toHaveProperty('Parameters.Overrides.Cpu', '1024'); +}); + +test('Running a task with memory parameter', () => { + const taskDefinition = new ecs.TaskDefinition(stack, 'TD', { + memoryMiB: '1024', + cpu: '512', + compatibility: ecs.Compatibility.FARGATE, + }); + const containerDefinition = taskDefinition.addContainer('TheContainer', { + containerName: 'ExplicitContainerName', + image: ecs.ContainerImage.fromRegistry('foo/bar'), + memoryLimitMiB: 256, + }); + + expect(stack.resolve( + new tasks.EcsRunTask(stack, 'task', { + cluster, + taskDefinition, + memoryMiB: '2048', + containerOverrides: [ + { + containerDefinition, + environment: [{ name: 'SOME_KEY', value: sfn.JsonPath.stringAt('$.SomeKey') }], + }, + ], + launchTarget: new tasks.EcsFargateLaunchTarget(), + }).toStateJson())).toHaveProperty('Parameters.Overrides.Memory', '2048'); +}); + test('Running a Fargate Task', () => { const taskDefinition = new ecs.TaskDefinition(stack, 'TD', { memoryMiB: '512',