From c860a9327777b51f0e456846eee8a9fbd1979b4c Mon Sep 17 00:00:00 2001 From: UnnatiParekh05 Date: Thu, 1 Apr 2021 16:01:00 -0700 Subject: [PATCH 1/9] feat(ecs): add support for elastic inference accelerators in ECS task definition --- .../aws-ecs/lib/base/task-definition.ts | 64 +++++++++++++ .../aws-ecs/lib/container-definition.ts | 89 +++++++++++++++++-- .../aws-ecs/lib/ec2/ec2-task-definition.ts | 13 ++- .../aws-ecs/test/container-definition.test.ts | 77 ++++++++++++++++ .../test/ec2/ec2-task-definition.test.ts | 27 ++++++ 5 files changed, 262 insertions(+), 8 deletions(-) diff --git a/packages/@aws-cdk/aws-ecs/lib/base/task-definition.ts b/packages/@aws-cdk/aws-ecs/lib/base/task-definition.ts index 7c3bb618142a5..0140b879807a1 100644 --- a/packages/@aws-cdk/aws-ecs/lib/base/task-definition.ts +++ b/packages/@aws-cdk/aws-ecs/lib/base/task-definition.ts @@ -184,6 +184,15 @@ export interface TaskDefinitionProps extends CommonTaskDefinitionProps { * @default - PidMode used by the task is not specified */ readonly pidMode?: PidMode; + + /** + * The inference accelerators to use for the containers in the task. + * + * Not supported in Fargate. + * + * @default - No inference accelerators. + */ + readonly inferenceAccelerators?: InferenceAccelerator[]; } /** @@ -322,6 +331,11 @@ export class TaskDefinition extends TaskDefinitionBase { */ private readonly placementConstraints = new Array(); + /** + * Inference accelerators for task instances + */ + public readonly inferenceAccelerators: InferenceAccelerator[] = []; + private _executionRole?: iam.IRole; private _referencesSecretJsonField?: boolean; @@ -354,6 +368,10 @@ export class TaskDefinition extends TaskDefinitionBase { throw new Error(`Fargate-compatible tasks require both CPU (${props.cpu}) and memory (${props.memoryMiB}) specifications`); } + if (props.inferenceAccelerators && props.inferenceAccelerators.length > 0 && this.isFargateCompatible) { + throw new Error('Cannot use inference accelerators on tasks that run on Fargate'); + } + this._executionRole = props.executionRole; this.taskRole = props.taskRole || new iam.Role(this, 'TaskRole', { @@ -380,12 +398,20 @@ export class TaskDefinition extends TaskDefinitionBase { memory: props.memoryMiB, ipcMode: props.ipcMode, pidMode: props.pidMode, + inferenceAccelerators: Lazy.any({ + produce: () => + !isFargateCompatible(this.compatibility) ? this.renderInferenceAccelerators() : undefined, + }, { omitEmptyArray: true }), }); if (props.placementConstraints) { props.placementConstraints.forEach(pc => this.addPlacementConstraint(pc)); } + if (props.inferenceAccelerators) { + props.inferenceAccelerators.forEach(ia => this.addInferenceAccelerator(ia)); + } + this.taskDefinitionArn = taskDef.ref; } @@ -419,6 +445,20 @@ export class TaskDefinition extends TaskDefinitionBase { } } + private renderInferenceAccelerators(): CfnTaskDefinition.InferenceAcceleratorProperty[] { + if (isFargateCompatible(this.compatibility)) { + throw new Error('Cannot use inference accelerators on tasks that run on Fargate'); + } + return this.inferenceAccelerators.map(renderInferenceAccelerator); + + function renderInferenceAccelerator(inferenceAccelerator: InferenceAccelerator) : CfnTaskDefinition.InferenceAcceleratorProperty { + return { + deviceName: inferenceAccelerator.deviceName, + deviceType: inferenceAccelerator.deviceType, + }; + } + } + /** * Validate the existence of the input target and set default values. * @@ -531,6 +571,13 @@ export class TaskDefinition extends TaskDefinitionBase { extension.extend(this); } + /** + * Adds an inference accelerator to the task definition. + */ + public addInferenceAccelerator(inferenceAccelerator: InferenceAccelerator) { + this.inferenceAccelerators.push(inferenceAccelerator); + } + /** * Creates the task execution IAM role if it doesn't already exist. */ @@ -683,6 +730,23 @@ export enum PidMode { TASK = 'task', } +/** + * Elastic Inference Accelerator. + */ +export interface InferenceAccelerator { + /** + * The Elastic Inference accelerator device name. + * @default - empty + */ + readonly deviceName?: string; + + /** + * The Elastic Inference accelerator type to use. + * @default - empty + */ + readonly deviceType?: string; +} + /** * A data volume used in a task definition. * diff --git a/packages/@aws-cdk/aws-ecs/lib/container-definition.ts b/packages/@aws-cdk/aws-ecs/lib/container-definition.ts index 9911a49a039cf..e3b06e09f1694 100644 --- a/packages/@aws-cdk/aws-ecs/lib/container-definition.ts +++ b/packages/@aws-cdk/aws-ecs/lib/container-definition.ts @@ -294,6 +294,12 @@ export interface ContainerDefinitionOptions { * @default - No ports are mapped. */ readonly portMappings?: PortMapping[]; + + /** + * The type and amount of a resource to assign to a container. + * @default - No resources assigned. + */ + readonly resourceRequirements?: ResourceRequirement[]; } /** @@ -386,6 +392,11 @@ export class ContainerDefinition extends CoreConstruct { */ public readonly referencesSecretJsonField?: boolean; + /** + * The type and amount of resource assigned to this container. + */ + private readonly resourceRequirements: ResourceRequirement[] = []; + /** * The configured container links */ @@ -443,6 +454,10 @@ export class ContainerDefinition extends CoreConstruct { if (props.portMappings) { this.addPortMappings(...props.portMappings); } + + if (props.resourceRequirements) { + this.addResourceRequirements(...props.resourceRequirements); + } } /** @@ -516,6 +531,25 @@ export class ContainerDefinition extends CoreConstruct { })); } + /** + * This method adds one or more resources to the container. + */ + private addResourceRequirements(...resourceRequirements: ResourceRequirement[]) { + if (!this.taskDefinition.inferenceAccelerators) { + throw new Error('InferenceAccelerator resource(s) defined in container definition without specifying any inference accelerators in task definition.'); + } + this.resourceRequirements.push(...resourceRequirements.map(resource => { + for (const inferenceAccelerator of this.taskDefinition.inferenceAccelerators) { + if (resource.value === inferenceAccelerator.deviceName) { + return { + ...resource, + }; + } + } + throw new Error(`Resource value (${resource.value}) doesn't match any inference accelerator device name.`); + })); + } + /** * This method adds one or more ulimits to the container. */ @@ -631,7 +665,7 @@ export class ContainerDefinition extends CoreConstruct { healthCheck: this.props.healthCheck && renderHealthCheck(this.props.healthCheck), links: cdk.Lazy.list({ produce: () => this.links }, { omitEmpty: true }), linuxParameters: this.linuxParameters && this.linuxParameters.renderLinuxParameters(), - resourceRequirements: (this.props.gpuCount !== undefined) ? renderResourceRequirements(this.props.gpuCount) : undefined, + resourceRequirements: renderResourceRequirements(this.props.gpuCount, this.resourceRequirements), }; } } @@ -742,12 +776,53 @@ function getHealthCheckCommand(hc: HealthCheck): string[] { return hcCommand.concat(cmd); } -function renderResourceRequirements(gpuCount: number): CfnTaskDefinition.ResourceRequirementProperty[] | undefined { - if (gpuCount === 0) { return undefined; } - return [{ - type: 'GPU', - value: gpuCount.toString(), - }]; +/** + * The type and amount of a resource to assign to a container. + */ +export interface ResourceRequirement { + /** + * The type of resource to assign to a container. + */ + readonly type: ResourceRequirementType, + + /** + * The value for the specified resource type. + */ + readonly value: string, +} + +/** Type of resource to assign to a container. */ +export enum ResourceRequirementType { + /** + * GPU. + */ + GPU = 'GPU', + /** + * InferenceAccelerator. + */ + INFERENCEACCELERATOR = 'InferenceAccelerator' +} + +function renderResourceRequirements(gpuCount: number = 0, resourceRequirements: ResourceRequirement[] = []): +CfnTaskDefinition.ResourceRequirementProperty[] | undefined { + if (resourceRequirements.length > 0) { + const ret = []; + + for (const resource of resourceRequirements) { + ret.push({ + type: ResourceRequirementType.INFERENCEACCELERATOR, + value: resource.value, + }); + } + return ret; + } + if (gpuCount > 0) { + return [{ + type: ResourceRequirementType.GPU, + value: gpuCount.toString(), + }]; + } + return undefined; } /** diff --git a/packages/@aws-cdk/aws-ecs/lib/ec2/ec2-task-definition.ts b/packages/@aws-cdk/aws-ecs/lib/ec2/ec2-task-definition.ts index ff571c884b73e..3b65516ba7dfa 100644 --- a/packages/@aws-cdk/aws-ecs/lib/ec2/ec2-task-definition.ts +++ b/packages/@aws-cdk/aws-ecs/lib/ec2/ec2-task-definition.ts @@ -1,4 +1,5 @@ import { Construct } from 'constructs'; +import { ImportedTaskDefinition } from '../base/_imported-task-definition'; import { CommonTaskDefinitionAttributes, CommonTaskDefinitionProps, @@ -8,9 +9,9 @@ import { NetworkMode, PidMode, TaskDefinition, + InferenceAccelerator, } from '../base/task-definition'; import { PlacementConstraint } from '../placement'; -import { ImportedTaskDefinition } from '../base/_imported-task-definition'; /** * The properties for a task definition run on an EC2 cluster. @@ -51,6 +52,15 @@ export interface Ec2TaskDefinitionProps extends CommonTaskDefinitionProps { * @default - PidMode used by the task is not specified */ readonly pidMode?: PidMode; + + /** + * The inference accelerators to use for the containers in the task. + * + * Not supported in Fargate. + * + * @default - No inference accelerators. + */ + readonly inferenceAccelerators?: InferenceAccelerator[]; } /** @@ -109,6 +119,7 @@ export class Ec2TaskDefinition extends TaskDefinition implements IEc2TaskDefinit placementConstraints: props.placementConstraints, ipcMode: props.ipcMode, pidMode: props.pidMode, + inferenceAccelerators: props.inferenceAccelerators, }); } } diff --git a/packages/@aws-cdk/aws-ecs/test/container-definition.test.ts b/packages/@aws-cdk/aws-ecs/test/container-definition.test.ts index 88fa5545c830e..548e36fd0eae0 100644 --- a/packages/@aws-cdk/aws-ecs/test/container-definition.test.ts +++ b/packages/@aws-cdk/aws-ecs/test/container-definition.test.ts @@ -997,6 +997,83 @@ describe('container definition', () => { }); }); + describe('Given InferenceAccelerator resource requirement', () => { + test('correctly adds resource requirements to container definition using props', () => { + // GIVEN + const stack = new cdk.Stack(); + + const inferenceAccelerator = [{ + deviceName: 'device1', + deviceType: 'eia2.medium', + }]; + + const taskDefinition = new ecs.Ec2TaskDefinition(stack, 'Ec2TaskDef', { + inferenceAccelerators: inferenceAccelerator, + }); + + const resourceRequirements = [{ + type: ecs.ResourceRequirementType.INFERENCEACCELERATOR, + value: 'device1', + }]; + + // WHEN + taskDefinition.addContainer('cont', { + image: ecs.ContainerImage.fromRegistry('test'), + memoryLimitMiB: 1024, + resourceRequirements: resourceRequirements, + }); + + // THEN + expect(stack).toHaveResourceLike('AWS::ECS::TaskDefinition', { + Family: 'Ec2TaskDef', + InferenceAccelerators: [{ + DeviceName: 'device1', + DeviceType: 'eia2.medium', + }], + ContainerDefinitions: [ + { + Image: 'test', + ResourceRequirements: [ + { + Type: 'InferenceAccelerator', + Value: 'device1', + }, + ], + }, + ], + }); + + + }); + test('throws when the value in resource requirement does not match any inference accelerators defined in the Task Definition', () => { + // GIVEN + const stack = new cdk.Stack(); + + const inferenceAccelerator = [{ + deviceName: 'device1', + deviceType: 'eia2.medium', + }]; + + const taskDefinition = new ecs.Ec2TaskDefinition(stack, 'Ec2TaskDef', { + inferenceAccelerators: inferenceAccelerator, + }); + + const resourceRequirements = [{ + type: ecs.ResourceRequirementType.INFERENCEACCELERATOR, + value: 'device2', + }]; + + // THEN + expect(() => { + taskDefinition.addContainer('cont', { + image: ecs.ContainerImage.fromRegistry('test'), + memoryLimitMiB: 1024, + resourceRequirements: resourceRequirements, + }); + }).toThrow(); + }); + }); + test('can add secret environment variables to the container definition', () => { // GIVEN const stack = new cdk.Stack(); diff --git a/packages/@aws-cdk/aws-ecs/test/ec2/ec2-task-definition.test.ts b/packages/@aws-cdk/aws-ecs/test/ec2/ec2-task-definition.test.ts index 4f713b06d2d71..ff60001241a74 100644 --- a/packages/@aws-cdk/aws-ecs/test/ec2/ec2-task-definition.test.ts +++ b/packages/@aws-cdk/aws-ecs/test/ec2/ec2-task-definition.test.ts @@ -1199,6 +1199,33 @@ describe('ec2 task definition', () => { }); }); + test('correctly sets inferenceAccelerators', () => { + // GIVEN + const stack = new cdk.Stack(); + const inferenceAccelerator = [{ + deviceName: 'device1', + deviceType: 'eia2.medium', + }]; + + const taskDefinition = new ecs.Ec2TaskDefinition(stack, 'Ec2TaskDef', { + inferenceAccelerators: inferenceAccelerator, + }); + + taskDefinition.addContainer('web', { + image: ecs.ContainerImage.fromRegistry('amazon/amazon-ecs-sample'), + memoryLimitMiB: 512, + }); + + // THEN + expect(stack).toHaveResourceLike('AWS::ECS::TaskDefinition', { + Family: 'Ec2TaskDef', + InferenceAccelerators: [{ + DeviceName: 'device1', + DeviceType: 'eia2.medium', + }], + }); + }); + describe('When importing from an existing Ec2 TaskDefinition', () => { test('can succeed using TaskDefinition Arn', () => { // GIVEN From f25ad20c1adf7b27c313dc6567f86f1bcc22dbc9 Mon Sep 17 00:00:00 2001 From: UnnatiParekh05 Date: Thu, 1 Apr 2021 16:01:40 -0700 Subject: [PATCH 2/9] docs: update README --- packages/@aws-cdk/aws-ecs/README.md | 36 +++++++++++++++++++++++++++++ 1 file changed, 36 insertions(+) diff --git a/packages/@aws-cdk/aws-ecs/README.md b/packages/@aws-cdk/aws-ecs/README.md index 5ad2855c8f642..3e2435a8420a3 100644 --- a/packages/@aws-cdk/aws-ecs/README.md +++ b/packages/@aws-cdk/aws-ecs/README.md @@ -788,3 +788,39 @@ new ecs.FargateService(stack, 'FargateService', { app.synth(); ``` + +## Elastic Inference Accelerators + +Currently, not supported on Fargate. + +To add elastic inference accelerators to your EC2 instance, first add +`inferenceAccelerator` field to the EC2TaskDefinition and set the `deviceName` +and `deviceType` properties. + +```ts +const inferenceAccelerator = [{ + deviceName: 'device1', + deviceType: 'eia2.medium', +}]; + +const taskDefinition = new ecs.Ec2TaskDefinition(stack, 'Ec2TaskDef', { + inferenceAccelerators: inferenceAccelerator, +}); +``` + +To enable using the inference accelerator in the containers, set the +`type` and `value` properties accordingly. The `value` should match the +`DeviceName` for an `InferenceAccelerator` specified in a task definition. + +```ts +const resourceRequirements = [{ + type: ecs.ResourceRequirementType.INFERENCEACCELERATOR, + value: 'device1', +}]; + +taskDefinition.addContainer('cont', { + image: ecs.ContainerImage.fromRegistry('test'), + memoryLimitMiB: 1024, + resourceRequirements: resourceRequirements, +}); +``` From 809e72e6f32e30a6e500b499f53bdf52ed336e37 Mon Sep 17 00:00:00 2001 From: Unnati Parekh <80710604+upparekh@users.noreply.github.com> Date: Fri, 2 Apr 2021 14:03:01 -0700 Subject: [PATCH 3/9] Apply suggestions from code review Updates to documentation and allocation of props in tests Co-authored-by: Hsing-Hui Hsu --- packages/@aws-cdk/aws-ecs/README.md | 8 ++++---- packages/@aws-cdk/aws-ecs/lib/container-definition.ts | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/packages/@aws-cdk/aws-ecs/README.md b/packages/@aws-cdk/aws-ecs/README.md index 3e2435a8420a3..57f40e6733a99 100644 --- a/packages/@aws-cdk/aws-ecs/README.md +++ b/packages/@aws-cdk/aws-ecs/README.md @@ -791,20 +791,20 @@ app.synth(); ## Elastic Inference Accelerators -Currently, not supported on Fargate. +Currently, this feature is only supported for servics with EC2 launch types. To add elastic inference accelerators to your EC2 instance, first add `inferenceAccelerator` field to the EC2TaskDefinition and set the `deviceName` and `deviceType` properties. ```ts -const inferenceAccelerator = [{ +const inferenceAccelerators = [{ deviceName: 'device1', deviceType: 'eia2.medium', }]; const taskDefinition = new ecs.Ec2TaskDefinition(stack, 'Ec2TaskDef', { - inferenceAccelerators: inferenceAccelerator, + inferenceAccelerators, }); ``` @@ -821,6 +821,6 @@ const resourceRequirements = [{ taskDefinition.addContainer('cont', { image: ecs.ContainerImage.fromRegistry('test'), memoryLimitMiB: 1024, - resourceRequirements: resourceRequirements, + resourceRequirements, }); ``` diff --git a/packages/@aws-cdk/aws-ecs/lib/container-definition.ts b/packages/@aws-cdk/aws-ecs/lib/container-definition.ts index 957cae8799dec..c04656656c797 100644 --- a/packages/@aws-cdk/aws-ecs/lib/container-definition.ts +++ b/packages/@aws-cdk/aws-ecs/lib/container-definition.ts @@ -800,7 +800,7 @@ export enum ResourceRequirementType { /** * InferenceAccelerator. */ - INFERENCEACCELERATOR = 'InferenceAccelerator' + INFERENCE_ACCELERATOR = 'InferenceAccelerator' } function renderResourceRequirements(gpuCount: number = 0, resourceRequirements: ResourceRequirement[] = []): From 2eca0bbf4fbdbc89f38a206e2b4c86422e634a58 Mon Sep 17 00:00:00 2001 From: Unnati Parekh <80710604+upparekh@users.noreply.github.com> Date: Mon, 5 Apr 2021 11:01:33 -0700 Subject: [PATCH 4/9] Apply suggestions from code review 2 Updated docs and sanity check condition Co-authored-by: Penghao He --- packages/@aws-cdk/aws-ecs/README.md | 2 +- packages/@aws-cdk/aws-ecs/lib/base/task-definition.ts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/@aws-cdk/aws-ecs/README.md b/packages/@aws-cdk/aws-ecs/README.md index 57f40e6733a99..4f19e2e1c915f 100644 --- a/packages/@aws-cdk/aws-ecs/README.md +++ b/packages/@aws-cdk/aws-ecs/README.md @@ -791,7 +791,7 @@ app.synth(); ## Elastic Inference Accelerators -Currently, this feature is only supported for servics with EC2 launch types. +Currently, this feature is only supported for services with EC2 launch types. To add elastic inference accelerators to your EC2 instance, first add `inferenceAccelerator` field to the EC2TaskDefinition and set the `deviceName` diff --git a/packages/@aws-cdk/aws-ecs/lib/base/task-definition.ts b/packages/@aws-cdk/aws-ecs/lib/base/task-definition.ts index 0140b879807a1..ba7f7ef10db79 100644 --- a/packages/@aws-cdk/aws-ecs/lib/base/task-definition.ts +++ b/packages/@aws-cdk/aws-ecs/lib/base/task-definition.ts @@ -368,7 +368,7 @@ export class TaskDefinition extends TaskDefinitionBase { throw new Error(`Fargate-compatible tasks require both CPU (${props.cpu}) and memory (${props.memoryMiB}) specifications`); } - if (props.inferenceAccelerators && props.inferenceAccelerators.length > 0 && this.isFargateCompatible) { + if (props.inferenceAccelerators?.length > 0 && this.isFargateCompatible) { throw new Error('Cannot use inference accelerators on tasks that run on Fargate'); } From dd60de8dae8c3686aa9f7335de2180fd02791176 Mon Sep 17 00:00:00 2001 From: UnnatiParekh05 Date: Tue, 6 Apr 2021 10:22:04 -0700 Subject: [PATCH 5/9] Replaced resourceRequirements prop to inferenceAcceleratorResources and updated tests --- .../aws-ecs/lib/base/task-definition.ts | 32 +++++--- .../aws-ecs/lib/container-definition.ts | 70 +++++----------- .../aws-ecs/test/container-definition.test.ts | 79 ++++++++++++++---- .../test/ec2/ec2-task-definition.test.ts | 80 ++++++++++++++----- .../fargate/fargate-task-definition.test.ts | 18 +++++ 5 files changed, 184 insertions(+), 95 deletions(-) diff --git a/packages/@aws-cdk/aws-ecs/lib/base/task-definition.ts b/packages/@aws-cdk/aws-ecs/lib/base/task-definition.ts index ba7f7ef10db79..3e7f82160d3fa 100644 --- a/packages/@aws-cdk/aws-ecs/lib/base/task-definition.ts +++ b/packages/@aws-cdk/aws-ecs/lib/base/task-definition.ts @@ -334,7 +334,7 @@ export class TaskDefinition extends TaskDefinitionBase { /** * Inference accelerators for task instances */ - public readonly inferenceAccelerators: InferenceAccelerator[] = []; + private readonly _inferenceAccelerators: InferenceAccelerator[] = []; private _executionRole?: iam.IRole; @@ -368,7 +368,7 @@ export class TaskDefinition extends TaskDefinitionBase { throw new Error(`Fargate-compatible tasks require both CPU (${props.cpu}) and memory (${props.memoryMiB}) specifications`); } - if (props.inferenceAccelerators?.length > 0 && this.isFargateCompatible) { + if (props.inferenceAccelerators && props.inferenceAccelerators.length > 0 && this.isFargateCompatible) { throw new Error('Cannot use inference accelerators on tasks that run on Fargate'); } @@ -378,6 +378,10 @@ export class TaskDefinition extends TaskDefinitionBase { assumedBy: new iam.ServicePrincipal('ecs-tasks.amazonaws.com'), }); + if (props.inferenceAccelerators) { + props.inferenceAccelerators.forEach(ia => this.addInferenceAccelerator(ia)); + } + const taskDef = new CfnTaskDefinition(this, 'Resource', { containerDefinitions: Lazy.any({ produce: () => this.renderContainers() }, { omitEmptyArray: true }), volumes: Lazy.any({ produce: () => this.renderVolumes() }, { omitEmptyArray: true }), @@ -408,10 +412,6 @@ export class TaskDefinition extends TaskDefinitionBase { props.placementConstraints.forEach(pc => this.addPlacementConstraint(pc)); } - if (props.inferenceAccelerators) { - props.inferenceAccelerators.forEach(ia => this.addInferenceAccelerator(ia)); - } - this.taskDefinitionArn = taskDef.ref; } @@ -419,6 +419,13 @@ export class TaskDefinition extends TaskDefinitionBase { return this._executionRole; } + /** + * Public getter method to access list of inference accelerators attached to the instance. + */ + public get inferenceAccelerators(): InferenceAccelerator[] { + return this._inferenceAccelerators; + } + private renderVolumes(): CfnTaskDefinition.VolumeProperty[] { return this.volumes.map(renderVolume); @@ -446,10 +453,7 @@ export class TaskDefinition extends TaskDefinitionBase { } private renderInferenceAccelerators(): CfnTaskDefinition.InferenceAcceleratorProperty[] { - if (isFargateCompatible(this.compatibility)) { - throw new Error('Cannot use inference accelerators on tasks that run on Fargate'); - } - return this.inferenceAccelerators.map(renderInferenceAccelerator); + return this._inferenceAccelerators.map(renderInferenceAccelerator); function renderInferenceAccelerator(inferenceAccelerator: InferenceAccelerator) : CfnTaskDefinition.InferenceAcceleratorProperty { return { @@ -575,7 +579,10 @@ export class TaskDefinition extends TaskDefinitionBase { * Adds an inference accelerator to the task definition. */ public addInferenceAccelerator(inferenceAccelerator: InferenceAccelerator) { - this.inferenceAccelerators.push(inferenceAccelerator); + if (isFargateCompatible(this.compatibility)) { + throw new Error('Cannot use inference accelerators on tasks that run on Fargate'); + } + this._inferenceAccelerators.push(inferenceAccelerator); } /** @@ -732,6 +739,7 @@ export enum PidMode { /** * Elastic Inference Accelerator. + * For more information, see [Elastic Inference Basics](https://docs.aws.amazon.com/elastic-inference/latest/developerguide/basics.html) */ export interface InferenceAccelerator { /** @@ -741,7 +749,7 @@ export interface InferenceAccelerator { readonly deviceName?: string; /** - * The Elastic Inference accelerator type to use. + * The Elastic Inference accelerator type to use. The allowed values are: eia2.medium, eia2.large and eia2.xlarge. * @default - empty */ readonly deviceType?: string; diff --git a/packages/@aws-cdk/aws-ecs/lib/container-definition.ts b/packages/@aws-cdk/aws-ecs/lib/container-definition.ts index c04656656c797..12f068d21c656 100644 --- a/packages/@aws-cdk/aws-ecs/lib/container-definition.ts +++ b/packages/@aws-cdk/aws-ecs/lib/container-definition.ts @@ -296,10 +296,10 @@ export interface ContainerDefinitionOptions { readonly portMappings?: PortMapping[]; /** - * The type and amount of a resource to assign to a container. - * @default - No resources assigned. + * The inference accelerators referenced by the container. + * @default - No inference accelerators assigned. */ - readonly resourceRequirements?: ResourceRequirement[]; + readonly inferenceAcceleratorResources?: string[]; } /** @@ -393,9 +393,9 @@ export class ContainerDefinition extends CoreConstruct { public readonly referencesSecretJsonField?: boolean; /** - * The type and amount of resource assigned to this container. + * The inference accelerators referenced by this container. */ - private readonly resourceRequirements: ResourceRequirement[] = []; + private readonly inferenceAcceleratorResources: string[] = []; /** * The configured container links @@ -455,8 +455,8 @@ export class ContainerDefinition extends CoreConstruct { this.addPortMappings(...props.portMappings); } - if (props.resourceRequirements) { - this.addResourceRequirements(...props.resourceRequirements); + if (props.inferenceAcceleratorResources) { + this.addInferenceAcceleratorResource(...props.inferenceAcceleratorResources); } } @@ -534,19 +534,17 @@ export class ContainerDefinition extends CoreConstruct { /** * This method adds one or more resources to the container. */ - private addResourceRequirements(...resourceRequirements: ResourceRequirement[]) { + public addInferenceAcceleratorResource(...inferenceAcceleratorResources: string[]) { if (!this.taskDefinition.inferenceAccelerators) { throw new Error('InferenceAccelerator resource(s) defined in container definition without specifying any inference accelerators in task definition.'); } - this.resourceRequirements.push(...resourceRequirements.map(resource => { + this.inferenceAcceleratorResources.push(...inferenceAcceleratorResources.map(resource => { for (const inferenceAccelerator of this.taskDefinition.inferenceAccelerators) { - if (resource.value === inferenceAccelerator.deviceName) { - return { - ...resource, - }; + if (resource === inferenceAccelerator.deviceName) { + return resource; } } - throw new Error(`Resource value (${resource.value}) doesn't match any inference accelerator device name.`); + throw new Error(`Resource value (${resource}) doesn't match any inference accelerator device name.`); })); } @@ -665,7 +663,7 @@ export class ContainerDefinition extends CoreConstruct { healthCheck: this.props.healthCheck && renderHealthCheck(this.props.healthCheck), links: cdk.Lazy.list({ produce: () => this.links }, { omitEmpty: true }), linuxParameters: this.linuxParameters && this.linuxParameters.renderLinuxParameters(), - resourceRequirements: renderResourceRequirements(this.props.gpuCount, this.resourceRequirements), + resourceRequirements: renderResourceRequirements(this.props.gpuCount, this.inferenceAcceleratorResources), }; } } @@ -776,49 +774,25 @@ function getHealthCheckCommand(hc: HealthCheck): string[] { return hcCommand.concat(cmd); } -/** - * The type and amount of a resource to assign to a container. - */ -export interface ResourceRequirement { - /** - * The type of resource to assign to a container. - */ - readonly type: ResourceRequirementType, - - /** - * The value for the specified resource type. - */ - readonly value: string, -} - -/** Type of resource to assign to a container. */ -export enum ResourceRequirementType { - /** - * GPU. - */ - GPU = 'GPU', - /** - * InferenceAccelerator. - */ - INFERENCE_ACCELERATOR = 'InferenceAccelerator' -} - -function renderResourceRequirements(gpuCount: number = 0, resourceRequirements: ResourceRequirement[] = []): +function renderResourceRequirements(gpuCount: number = 0, inferenceAcceleratorResources: string[] = []): CfnTaskDefinition.ResourceRequirementProperty[] | undefined { - if (resourceRequirements.length > 0) { + if (inferenceAcceleratorResources.length > 0 && gpuCount > 0) { + throw new Error('Both inference accelerator and gpu count defined in the container definition.'); + } + if (inferenceAcceleratorResources.length > 0) { const ret = []; - for (const resource of resourceRequirements) { + for (const resource of inferenceAcceleratorResources) { ret.push({ - type: ResourceRequirementType.INFERENCEACCELERATOR, - value: resource.value, + type: 'InferenceAccelerator', + value: resource, }); } return ret; } if (gpuCount > 0) { return [{ - type: ResourceRequirementType.GPU, + type: 'GPU', value: gpuCount.toString(), }]; } diff --git a/packages/@aws-cdk/aws-ecs/test/container-definition.test.ts b/packages/@aws-cdk/aws-ecs/test/container-definition.test.ts index 548e36fd0eae0..aa71fdcb2106c 100644 --- a/packages/@aws-cdk/aws-ecs/test/container-definition.test.ts +++ b/packages/@aws-cdk/aws-ecs/test/container-definition.test.ts @@ -998,29 +998,26 @@ describe('container definition', () => { }); describe('Given InferenceAccelerator resource requirement', () => { - test('correctly adds resource requirements to container definition using props', () => { + test('correctly adds resource requirements to container definition using inference accelerator resource property', () => { // GIVEN const stack = new cdk.Stack(); - const inferenceAccelerator = [{ + const inferenceAccelerators = [{ deviceName: 'device1', deviceType: 'eia2.medium', }]; const taskDefinition = new ecs.Ec2TaskDefinition(stack, 'Ec2TaskDef', { - inferenceAccelerators: inferenceAccelerator, + inferenceAccelerators, }); - const resourceRequirements = [{ - type: ecs.ResourceRequirementType.INFERENCEACCELERATOR, - value: 'device1', - }]; + const inferenceAcceleratorResources = ['device1']; // WHEN taskDefinition.addContainer('cont', { image: ecs.ContainerImage.fromRegistry('test'), memoryLimitMiB: 1024, - resourceRequirements: resourceRequirements, + inferenceAcceleratorResources, }); // THEN @@ -1045,30 +1042,82 @@ describe('container definition', () => { }); - test('throws when the value in resource requirement does not match any inference accelerators defined in the Task Definition', () => { + test('correctly adds inference accelerator resources to container definition using both props and addInferenceAcceleratorResource method', () => { // GIVEN const stack = new cdk.Stack(); - const inferenceAccelerator = [{ + const inferenceAccelerators = [{ deviceName: 'device1', deviceType: 'eia2.medium', + }, { + deviceName: 'device2', + deviceType: 'eia2.large', }]; const taskDefinition = new ecs.Ec2TaskDefinition(stack, 'Ec2TaskDef', { - inferenceAccelerators: inferenceAccelerator, + inferenceAccelerators, + }); + + const inferenceAcceleratorResources = ['device1']; + + const container = taskDefinition.addContainer('cont', { + image: ecs.ContainerImage.fromRegistry('test'), + memoryLimitMiB: 1024, + inferenceAcceleratorResources, + }); + + // WHEN + container.addInferenceAcceleratorResource('device2'); + + // THEN + expect(stack).toHaveResourceLike('AWS::ECS::TaskDefinition', { + Family: 'Ec2TaskDef', + InferenceAccelerators: [{ + DeviceName: 'device1', + DeviceType: 'eia2.medium', + }, { + DeviceName: 'device2', + DeviceType: 'eia2.large', + }], + ContainerDefinitions: [ + { + Image: 'test', + ResourceRequirements: [ + { + Type: 'InferenceAccelerator', + Value: 'device1', + }, + { + Type: 'InferenceAccelerator', + Value: 'device2', + }, + ], + }, + ], }); - const resourceRequirements = [{ - type: ecs.ResourceRequirementType.INFERENCEACCELERATOR, - value: 'device2', + }); + test('throws when the value in resource requirement does not match any inference accelerators defined in the Task Definition', () => { + // GIVEN + const stack = new cdk.Stack(); + + const inferenceAccelerators = [{ + deviceName: 'device1', + deviceType: 'eia2.medium', }]; + const taskDefinition = new ecs.Ec2TaskDefinition(stack, 'Ec2TaskDef', { + inferenceAccelerators, + }); + + const inferenceAcceleratorResources = ['device2']; + // THEN expect(() => { taskDefinition.addContainer('cont', { image: ecs.ContainerImage.fromRegistry('test'), memoryLimitMiB: 1024, - resourceRequirements: resourceRequirements, + inferenceAcceleratorResources, }); }).toThrow(); }); diff --git a/packages/@aws-cdk/aws-ecs/test/ec2/ec2-task-definition.test.ts b/packages/@aws-cdk/aws-ecs/test/ec2/ec2-task-definition.test.ts index ff60001241a74..fe5e1d9ef2304 100644 --- a/packages/@aws-cdk/aws-ecs/test/ec2/ec2-task-definition.test.ts +++ b/packages/@aws-cdk/aws-ecs/test/ec2/ec2-task-definition.test.ts @@ -1199,30 +1199,70 @@ describe('ec2 task definition', () => { }); }); - test('correctly sets inferenceAccelerators', () => { - // GIVEN - const stack = new cdk.Stack(); - const inferenceAccelerator = [{ - deviceName: 'device1', - deviceType: 'eia2.medium', - }]; + describe('setting inferenceAccelerators', () => { + test('correctly sets inferenceAccelerators using props', () => { + // GIVEN + const stack = new cdk.Stack(); + const inferenceAccelerators = [{ + deviceName: 'device1', + deviceType: 'eia2.medium', + }]; - const taskDefinition = new ecs.Ec2TaskDefinition(stack, 'Ec2TaskDef', { - inferenceAccelerators: inferenceAccelerator, - }); + // WHEN + const taskDefinition = new ecs.Ec2TaskDefinition(stack, 'Ec2TaskDef', { + inferenceAccelerators, + }); + + taskDefinition.addContainer('web', { + image: ecs.ContainerImage.fromRegistry('amazon/amazon-ecs-sample'), + memoryLimitMiB: 512, + }); + + // THEN + expect(stack).toHaveResourceLike('AWS::ECS::TaskDefinition', { + Family: 'Ec2TaskDef', + InferenceAccelerators: [{ + DeviceName: 'device1', + DeviceType: 'eia2.medium', + }], + }); - taskDefinition.addContainer('web', { - image: ecs.ContainerImage.fromRegistry('amazon/amazon-ecs-sample'), - memoryLimitMiB: 512, }); + test('correctly sets inferenceAccelerators using props and addInferenceAccelerator method', () => { + // GIVEN + const stack = new cdk.Stack(); + const inferenceAccelerators = [{ + deviceName: 'device1', + deviceType: 'eia2.medium', + }]; + + const taskDefinition = new ecs.Ec2TaskDefinition(stack, 'Ec2TaskDef', { + inferenceAccelerators, + }); + + // WHEN + taskDefinition.addInferenceAccelerator({ + deviceName: 'device2', + deviceType: 'eia2.large', + }); + + taskDefinition.addContainer('web', { + image: ecs.ContainerImage.fromRegistry('amazon/amazon-ecs-sample'), + memoryLimitMiB: 512, + }); + + // THEN + expect(stack).toHaveResourceLike('AWS::ECS::TaskDefinition', { + Family: 'Ec2TaskDef', + InferenceAccelerators: [{ + DeviceName: 'device1', + DeviceType: 'eia2.medium', + }, { + DeviceName: 'device2', + DeviceType: 'eia2.large', + }], + }); - // THEN - expect(stack).toHaveResourceLike('AWS::ECS::TaskDefinition', { - Family: 'Ec2TaskDef', - InferenceAccelerators: [{ - DeviceName: 'device1', - DeviceType: 'eia2.medium', - }], }); }); diff --git a/packages/@aws-cdk/aws-ecs/test/fargate/fargate-task-definition.test.ts b/packages/@aws-cdk/aws-ecs/test/fargate/fargate-task-definition.test.ts index b8fad1bc9316f..2677c5674ae55 100644 --- a/packages/@aws-cdk/aws-ecs/test/fargate/fargate-task-definition.test.ts +++ b/packages/@aws-cdk/aws-ecs/test/fargate/fargate-task-definition.test.ts @@ -113,6 +113,24 @@ nodeunitShim({ test.done(); }, + + 'throws when adding inference accelerators'(test: Test) { + // GIVEN + const stack = new cdk.Stack(); + const taskDefinition = new ecs.FargateTaskDefinition(stack, 'FargateTaskDef'); + + const inferenceAccelerator = { + deviceName: 'device1', + deviceType: 'eia2.medium', + }; + + // THEN + test.throws(() => { + taskDefinition.addInferenceAccelerator(inferenceAccelerator); + }, /Cannot use inference accelerators on tasks that run on Fargate/); + + test.done(); + }, }, 'When importing from an existing Fargate TaskDefinition': { From dd28b7a8196ec0dfa47c8e5e74d070d21156a7b3 Mon Sep 17 00:00:00 2001 From: Unnati Parekh <80710604+upparekh@users.noreply.github.com> Date: Fri, 9 Apr 2021 09:55:53 -0700 Subject: [PATCH 6/9] Apply suggestions from code review Updating error messages Co-authored-by: Penghao He --- packages/@aws-cdk/aws-ecs/lib/container-definition.ts | 4 ++-- .../@aws-cdk/aws-ecs/test/ec2/ec2-task-definition.test.ts | 1 - 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/packages/@aws-cdk/aws-ecs/lib/container-definition.ts b/packages/@aws-cdk/aws-ecs/lib/container-definition.ts index 12f068d21c656..237421e23c881 100644 --- a/packages/@aws-cdk/aws-ecs/lib/container-definition.ts +++ b/packages/@aws-cdk/aws-ecs/lib/container-definition.ts @@ -544,7 +544,7 @@ export class ContainerDefinition extends CoreConstruct { return resource; } } - throw new Error(`Resource value (${resource}) doesn't match any inference accelerator device name.`); + throw new Error(`Resource value (${resource}) doesn't match any inference accelerator device name defined in task definition.`); })); } @@ -777,7 +777,7 @@ function getHealthCheckCommand(hc: HealthCheck): string[] { function renderResourceRequirements(gpuCount: number = 0, inferenceAcceleratorResources: string[] = []): CfnTaskDefinition.ResourceRequirementProperty[] | undefined { if (inferenceAcceleratorResources.length > 0 && gpuCount > 0) { - throw new Error('Both inference accelerator and gpu count defined in the container definition.'); + throw new Error('Cannot define both inference accelerator and gpu count in the container definition.'); } if (inferenceAcceleratorResources.length > 0) { const ret = []; diff --git a/packages/@aws-cdk/aws-ecs/test/ec2/ec2-task-definition.test.ts b/packages/@aws-cdk/aws-ecs/test/ec2/ec2-task-definition.test.ts index dd52be8917d62..06f891868feb8 100644 --- a/packages/@aws-cdk/aws-ecs/test/ec2/ec2-task-definition.test.ts +++ b/packages/@aws-cdk/aws-ecs/test/ec2/ec2-task-definition.test.ts @@ -1262,7 +1262,6 @@ describe('ec2 task definition', () => { DeviceType: 'eia2.large', }], }); - }); }); From d640f7ad186c3fa3beac420d00c9b003feb7c057 Mon Sep 17 00:00:00 2001 From: UnnatiParekh05 Date: Fri, 9 Apr 2021 16:37:46 -0700 Subject: [PATCH 7/9] Added and updated unit tests --- .../aws-ecs/lib/container-definition.ts | 5 +-- .../aws-ecs/test/container-definition.test.ts | 35 ++++++++++++++++++- 2 files changed, 35 insertions(+), 5 deletions(-) diff --git a/packages/@aws-cdk/aws-ecs/lib/container-definition.ts b/packages/@aws-cdk/aws-ecs/lib/container-definition.ts index 237421e23c881..f96fc78f24dbd 100644 --- a/packages/@aws-cdk/aws-ecs/lib/container-definition.ts +++ b/packages/@aws-cdk/aws-ecs/lib/container-definition.ts @@ -535,16 +535,13 @@ export class ContainerDefinition extends CoreConstruct { * This method adds one or more resources to the container. */ public addInferenceAcceleratorResource(...inferenceAcceleratorResources: string[]) { - if (!this.taskDefinition.inferenceAccelerators) { - throw new Error('InferenceAccelerator resource(s) defined in container definition without specifying any inference accelerators in task definition.'); - } this.inferenceAcceleratorResources.push(...inferenceAcceleratorResources.map(resource => { for (const inferenceAccelerator of this.taskDefinition.inferenceAccelerators) { if (resource === inferenceAccelerator.deviceName) { return resource; } } - throw new Error(`Resource value (${resource}) doesn't match any inference accelerator device name defined in task definition.`); + throw new Error(`Resource value ${resource} in container definition doesn't match any inference accelerator device name in the task definition.`); })); } diff --git a/packages/@aws-cdk/aws-ecs/test/container-definition.test.ts b/packages/@aws-cdk/aws-ecs/test/container-definition.test.ts index b735988335883..ef971901a58a2 100644 --- a/packages/@aws-cdk/aws-ecs/test/container-definition.test.ts +++ b/packages/@aws-cdk/aws-ecs/test/container-definition.test.ts @@ -1119,7 +1119,40 @@ describe('container definition', () => { memoryLimitMiB: 1024, inferenceAcceleratorResources, }); - }).toThrow(); + }).toThrow(/Resource value device2 in container definition doesn't match any inference accelerator device name in the task definition./); + }); + test('throws when both inference accelerator and gpu count are defined in the container definition', () => { + // GIVEN + const stack = new cdk.Stack(); + + const inferenceAccelerators = [{ + deviceName: 'device1', + deviceType: 'eia2.medium', + }]; + + const taskDefinition = new ecs.Ec2TaskDefinition(stack, 'Ec2TaskDef', { + inferenceAccelerators, + }); + + const inferenceAcceleratorResources = ['device1']; + + taskDefinition.addContainer('cont', { + image: ecs.ContainerImage.fromRegistry('test'), + memoryLimitMiB: 1024, + gpuCount: 2, + inferenceAcceleratorResources, + }); + + // THEN + expect(() => { + expect(stack).toHaveResourceLike('AWS::ECS::TaskDefinition', { + ContainerDefinitions: [ + { + inferenceAcceleratorResources, + }, + ], + }); + }).toThrow(/Cannot define both inference accelerator and gpu count in the container definition./); }); }); From 0aaaab62ccd33af3534da342432815daac2a4f02 Mon Sep 17 00:00:00 2001 From: UnnatiParekh05 Date: Wed, 14 Apr 2021 09:26:56 -0700 Subject: [PATCH 8/9] Render both gpu count and inference accelerator resources instead of throwing error --- .../aws-ecs/lib/container-definition.ts | 16 ++--- .../aws-ecs/test/container-definition.test.ts | 67 ++++++++++--------- 2 files changed, 43 insertions(+), 40 deletions(-) diff --git a/packages/@aws-cdk/aws-ecs/lib/container-definition.ts b/packages/@aws-cdk/aws-ecs/lib/container-definition.ts index f96fc78f24dbd..4f01764f64ee8 100644 --- a/packages/@aws-cdk/aws-ecs/lib/container-definition.ts +++ b/packages/@aws-cdk/aws-ecs/lib/container-definition.ts @@ -660,7 +660,8 @@ export class ContainerDefinition extends CoreConstruct { healthCheck: this.props.healthCheck && renderHealthCheck(this.props.healthCheck), links: cdk.Lazy.list({ produce: () => this.links }, { omitEmpty: true }), linuxParameters: this.linuxParameters && this.linuxParameters.renderLinuxParameters(), - resourceRequirements: renderResourceRequirements(this.props.gpuCount, this.inferenceAcceleratorResources), + resourceRequirements: (!this.props.gpuCount && this.inferenceAcceleratorResources.length == 0 ) ? undefined : + renderResourceRequirements(this.props.gpuCount, this.inferenceAcceleratorResources), }; } } @@ -773,27 +774,22 @@ function getHealthCheckCommand(hc: HealthCheck): string[] { function renderResourceRequirements(gpuCount: number = 0, inferenceAcceleratorResources: string[] = []): CfnTaskDefinition.ResourceRequirementProperty[] | undefined { - if (inferenceAcceleratorResources.length > 0 && gpuCount > 0) { - throw new Error('Cannot define both inference accelerator and gpu count in the container definition.'); - } + const ret = []; if (inferenceAcceleratorResources.length > 0) { - const ret = []; - for (const resource of inferenceAcceleratorResources) { ret.push({ type: 'InferenceAccelerator', value: resource, }); } - return ret; } if (gpuCount > 0) { - return [{ + ret.push({ type: 'GPU', value: gpuCount.toString(), - }]; + }); } - return undefined; + return ret; } /** diff --git a/packages/@aws-cdk/aws-ecs/test/container-definition.test.ts b/packages/@aws-cdk/aws-ecs/test/container-definition.test.ts index ef971901a58a2..a110046c71bf1 100644 --- a/packages/@aws-cdk/aws-ecs/test/container-definition.test.ts +++ b/packages/@aws-cdk/aws-ecs/test/container-definition.test.ts @@ -997,7 +997,7 @@ describe('container definition', () => { }); }); - describe('Given InferenceAccelerator resource requirement', () => { + describe('Given InferenceAccelerator resource parameter', () => { test('correctly adds resource requirements to container definition using inference accelerator resource property', () => { // GIVEN const stack = new cdk.Stack(); @@ -1042,7 +1042,7 @@ describe('container definition', () => { }); - test('correctly adds inference accelerator resources to container definition using both props and addInferenceAcceleratorResource method', () => { + test('correctly adds resource requirements to container definition using both props and addInferenceAcceleratorResource method', () => { // GIVEN const stack = new cdk.Stack(); @@ -1097,7 +1097,7 @@ describe('container definition', () => { }); }); - test('throws when the value in resource requirement does not match any inference accelerators defined in the Task Definition', () => { + test('throws when the value of inference accelerator resource does not match any inference accelerators defined in the Task Definition', () => { // GIVEN const stack = new cdk.Stack(); @@ -1121,38 +1121,45 @@ describe('container definition', () => { }); }).toThrow(/Resource value device2 in container definition doesn't match any inference accelerator device name in the task definition./); }); - test('throws when both inference accelerator and gpu count are defined in the container definition', () => { - // GIVEN - const stack = new cdk.Stack(); + }); - const inferenceAccelerators = [{ - deviceName: 'device1', - deviceType: 'eia2.medium', - }]; + test('adds resource requirements when both inference accelerator and gpu count are defined in the container definition', () => { + // GIVEN + const stack = new cdk.Stack(); - const taskDefinition = new ecs.Ec2TaskDefinition(stack, 'Ec2TaskDef', { - inferenceAccelerators, - }); + const inferenceAccelerators = [{ + deviceName: 'device1', + deviceType: 'eia2.medium', + }]; - const inferenceAcceleratorResources = ['device1']; + const taskDefinition = new ecs.Ec2TaskDefinition(stack, 'Ec2TaskDef', { + inferenceAccelerators, + }); - taskDefinition.addContainer('cont', { - image: ecs.ContainerImage.fromRegistry('test'), - memoryLimitMiB: 1024, - gpuCount: 2, - inferenceAcceleratorResources, - }); + const inferenceAcceleratorResources = ['device1']; - // THEN - expect(() => { - expect(stack).toHaveResourceLike('AWS::ECS::TaskDefinition', { - ContainerDefinitions: [ - { - inferenceAcceleratorResources, - }, - ], - }); - }).toThrow(/Cannot define both inference accelerator and gpu count in the container definition./); + taskDefinition.addContainer('cont', { + image: ecs.ContainerImage.fromRegistry('test'), + memoryLimitMiB: 1024, + gpuCount: 2, + inferenceAcceleratorResources, + }); + + // THEN + expect(() => { + expect(stack).toHaveResourceLike('AWS::ECS::TaskDefinition', { + ContainerDefinitions: [ + { + reourceRequirements: [{ + type: 'InferenceAccelerator', + value: 'device1', + }, { + type: 'GPU', + value: '2', + }], + }, + ], + }); }); }); From 89a20bdc831816e595022694a0138a0d7688ca5b Mon Sep 17 00:00:00 2001 From: UnnatiParekh05 Date: Wed, 14 Apr 2021 11:19:29 -0700 Subject: [PATCH 9/9] Updated unit test --- .../aws-ecs/lib/container-definition.ts | 12 +++---- .../aws-ecs/test/container-definition.test.ts | 32 +++++++++++-------- 2 files changed, 23 insertions(+), 21 deletions(-) diff --git a/packages/@aws-cdk/aws-ecs/lib/container-definition.ts b/packages/@aws-cdk/aws-ecs/lib/container-definition.ts index 4f01764f64ee8..8f57e84872de5 100644 --- a/packages/@aws-cdk/aws-ecs/lib/container-definition.ts +++ b/packages/@aws-cdk/aws-ecs/lib/container-definition.ts @@ -775,13 +775,11 @@ function getHealthCheckCommand(hc: HealthCheck): string[] { function renderResourceRequirements(gpuCount: number = 0, inferenceAcceleratorResources: string[] = []): CfnTaskDefinition.ResourceRequirementProperty[] | undefined { const ret = []; - if (inferenceAcceleratorResources.length > 0) { - for (const resource of inferenceAcceleratorResources) { - ret.push({ - type: 'InferenceAccelerator', - value: resource, - }); - } + for (const resource of inferenceAcceleratorResources) { + ret.push({ + type: 'InferenceAccelerator', + value: resource, + }); } if (gpuCount > 0) { ret.push({ diff --git a/packages/@aws-cdk/aws-ecs/test/container-definition.test.ts b/packages/@aws-cdk/aws-ecs/test/container-definition.test.ts index a110046c71bf1..c5d6ece5776d3 100644 --- a/packages/@aws-cdk/aws-ecs/test/container-definition.test.ts +++ b/packages/@aws-cdk/aws-ecs/test/container-definition.test.ts @@ -1146,20 +1146,24 @@ describe('container definition', () => { }); // THEN - expect(() => { - expect(stack).toHaveResourceLike('AWS::ECS::TaskDefinition', { - ContainerDefinitions: [ - { - reourceRequirements: [{ - type: 'InferenceAccelerator', - value: 'device1', - }, { - type: 'GPU', - value: '2', - }], - }, - ], - }); + expect(stack).toHaveResourceLike('AWS::ECS::TaskDefinition', { + Family: 'Ec2TaskDef', + InferenceAccelerators: [{ + DeviceName: 'device1', + DeviceType: 'eia2.medium', + }], + ContainerDefinitions: [ + { + Image: 'test', + ResourceRequirements: [{ + Type: 'InferenceAccelerator', + Value: 'device1', + }, { + Type: 'GPU', + Value: '2', + }], + }, + ], }); });