From 93ff8561636369a2f90627f78665a415269a5a59 Mon Sep 17 00:00:00 2001 From: Luca Pizzini Date: Mon, 1 Apr 2024 10:19:20 +0200 Subject: [PATCH 1/2] feat(ecs): support pidMode for FargateTaskDefinition --- .../aws-ecs-integ-runtime.template.json | 1 + .../aws-ecs/test/fargate/integ.runtime.ts | 1 + packages/aws-cdk-lib/aws-ecs/README.md | 17 ++++++++++ .../aws-ecs/lib/base/task-definition.ts | 8 +++-- .../aws-ecs/lib/ec2/ec2-task-definition.ts | 2 +- .../lib/fargate/fargate-task-definition.ts | 22 ++++++++++++ .../fargate/fargate-task-definition.test.ts | 34 +++++++++++++++++++ 7 files changed, 81 insertions(+), 4 deletions(-) diff --git a/packages/@aws-cdk-testing/framework-integ/test/aws-ecs/test/fargate/integ.runtime.js.snapshot/aws-ecs-integ-runtime.template.json b/packages/@aws-cdk-testing/framework-integ/test/aws-ecs/test/fargate/integ.runtime.js.snapshot/aws-ecs-integ-runtime.template.json index b9b747f93c73e..0108491f8601d 100644 --- a/packages/@aws-cdk-testing/framework-integ/test/aws-ecs/test/fargate/integ.runtime.js.snapshot/aws-ecs-integ-runtime.template.json +++ b/packages/@aws-cdk-testing/framework-integ/test/aws-ecs/test/fargate/integ.runtime.js.snapshot/aws-ecs-integ-runtime.template.json @@ -570,6 +570,7 @@ "Family": "awsecsintegruntimeTaskDefGraviton28E28B263", "Memory": "1024", "NetworkMode": "awsvpc", + "PidMode": "host", "RequiresCompatibilities": [ "FARGATE" ], diff --git a/packages/@aws-cdk-testing/framework-integ/test/aws-ecs/test/fargate/integ.runtime.ts b/packages/@aws-cdk-testing/framework-integ/test/aws-ecs/test/fargate/integ.runtime.ts index f3d1038e6c9ba..8f224e13e86cd 100644 --- a/packages/@aws-cdk-testing/framework-integ/test/aws-ecs/test/fargate/integ.runtime.ts +++ b/packages/@aws-cdk-testing/framework-integ/test/aws-ecs/test/fargate/integ.runtime.ts @@ -27,6 +27,7 @@ const taskDefinitiongraviton2 = new ecs.FargateTaskDefinition(stack, 'TaskDefGra }, cpu: 256, memoryLimitMiB: 1024, + pidMode: ecs.PidMode.HOST, }); taskDefinitionwindows.addContainer('windowsservercore', { diff --git a/packages/aws-cdk-lib/aws-ecs/README.md b/packages/aws-cdk-lib/aws-ecs/README.md index 43ac276abe91c..dc1b4697308eb 100644 --- a/packages/aws-cdk-lib/aws-ecs/README.md +++ b/packages/aws-cdk-lib/aws-ecs/README.md @@ -362,6 +362,23 @@ const fargateTaskDefinition = new ecs.FargateTaskDefinition(this, 'TaskDef', { }); ``` +To specify the process namespace to use for the containers in the task, use the `pidMode` property: + +```ts +const fargateTaskDefinition = new ecs.FargateTaskDefinition(this, 'TaskDef', { + runtimePlatform: { + operatingSystemFamily: ecs.OperatingSystemFamily.LINUX, + cpuArchitecture: ecs.CpuArchitecture.ARM64, + }, + memoryLimitMiB: 512, + cpu: 256, + pidMode: ecs.PidMode.HOST, +}); +``` + +**Note:** `pidMode` is only supported for tasks that are hosted on AWS Fargate if the tasks are using platform version 1.4.0 +or later (Linux). This isn't supported for Windows containers on Fargate. + To add containers to a task definition, call `addContainer()`: ```ts diff --git a/packages/aws-cdk-lib/aws-ecs/lib/base/task-definition.ts b/packages/aws-cdk-lib/aws-ecs/lib/base/task-definition.ts index c675d9166f35d..be15cf6c195c5 100644 --- a/packages/aws-cdk-lib/aws-ecs/lib/base/task-definition.ts +++ b/packages/aws-cdk-lib/aws-ecs/lib/base/task-definition.ts @@ -193,7 +193,9 @@ export interface TaskDefinitionProps extends CommonTaskDefinitionProps { /** * The process namespace to use for the containers in the task. * - * Not supported in Fargate and Windows containers. + * Only supported for tasks that are hosted on AWS Fargate if the tasks + * are using platform version 1.4.0 or later (Linux). + * Not supported in Windows containers. * * @default - PidMode used by the task is not specified */ @@ -219,8 +221,8 @@ export interface TaskDefinitionProps extends CommonTaskDefinitionProps { /** * The operating system that your task definitions are running on. - * A runtimePlatform is supported only for tasks using the Fargate launch type. * + * A runtimePlatform is supported only for tasks using the Fargate launch type. * * @default - Undefined. */ @@ -455,7 +457,7 @@ export class TaskDefinition extends TaskDefinitionBase { this.ephemeralStorageGiB = props.ephemeralStorageGiB; // validate the cpu and memory size for the Windows operation system family. - if (props.runtimePlatform?.operatingSystemFamily?._operatingSystemFamily.includes('WINDOWS')) { + if (props.runtimePlatform?.operatingSystemFamily?.isWindows()) { // We know that props.cpu and props.memoryMiB are defined because an error would have been thrown previously if they were not. // But, typescript is not able to figure this out, so using the `!` operator here to let the type-checker know they are defined. this.checkFargateWindowsBasedTasksSize(props.cpu!, props.memoryMiB!, props.runtimePlatform!); diff --git a/packages/aws-cdk-lib/aws-ecs/lib/ec2/ec2-task-definition.ts b/packages/aws-cdk-lib/aws-ecs/lib/ec2/ec2-task-definition.ts index e4092f76d9cf5..a75284934598b 100644 --- a/packages/aws-cdk-lib/aws-ecs/lib/ec2/ec2-task-definition.ts +++ b/packages/aws-cdk-lib/aws-ecs/lib/ec2/ec2-task-definition.ts @@ -49,7 +49,7 @@ export interface Ec2TaskDefinitionProps extends CommonTaskDefinitionProps { /** * The process namespace to use for the containers in the task. * - * Not supported in Fargate and Windows containers. + * Not supported in Windows containers. * * @default - PidMode used by the task is not specified */ diff --git a/packages/aws-cdk-lib/aws-ecs/lib/fargate/fargate-task-definition.ts b/packages/aws-cdk-lib/aws-ecs/lib/fargate/fargate-task-definition.ts index bb42e4262764c..34d0bed643aa2 100644 --- a/packages/aws-cdk-lib/aws-ecs/lib/fargate/fargate-task-definition.ts +++ b/packages/aws-cdk-lib/aws-ecs/lib/fargate/fargate-task-definition.ts @@ -7,6 +7,7 @@ import { Compatibility, ITaskDefinition, NetworkMode, + PidMode, TaskDefinition, } from '../base/task-definition'; import { RuntimePlatform } from '../runtime-platform'; @@ -77,6 +78,17 @@ export interface FargateTaskDefinitionProps extends CommonTaskDefinitionProps { * @default - Undefined. */ readonly runtimePlatform?: RuntimePlatform; + + /** + * The process namespace to use for the containers in the task. + * + * Only supported for tasks that are hosted on AWS Fargate if the tasks + * are using platform version 1.4.0 or later (Linux). + * Not supported in Windows containers. + * + * @default - PidMode used by the task is not specified + */ + readonly pidMode?: PidMode; } /** @@ -147,6 +159,7 @@ export class FargateTaskDefinition extends TaskDefinition implements IFargateTas memoryMiB: props.memoryLimitMiB !== undefined ? Tokenization.stringifyNumber(props.memoryLimitMiB) : '512', compatibility: Compatibility.FARGATE, networkMode: NetworkMode.AWS_VPC, + pidMode: props.pidMode, }); // eslint-disable-next-line max-len @@ -154,6 +167,15 @@ export class FargateTaskDefinition extends TaskDefinition implements IFargateTas throw new Error('Ephemeral storage size must be between 21GiB and 200GiB'); } + if (props.pidMode) { + if (props.runtimePlatform?.operatingSystemFamily?.isWindows()) { + throw new Error('\'pidMode\' is not supported for Windows containers.'); + } + if (!Token.isUnresolved(props.pidMode) && props.pidMode !== PidMode.HOST) { + throw new Error(`\'pidMode\' can only be set to \'${PidMode.HOST}\' for Fargate containers, got: \'${props.pidMode}\'.`); + } + } + this.ephemeralStorageGiB = props.ephemeralStorageGiB; } } diff --git a/packages/aws-cdk-lib/aws-ecs/test/fargate/fargate-task-definition.test.ts b/packages/aws-cdk-lib/aws-ecs/test/fargate/fargate-task-definition.test.ts index df7beb1b3fec8..def9ba61aaea8 100644 --- a/packages/aws-cdk-lib/aws-ecs/test/fargate/fargate-task-definition.test.ts +++ b/packages/aws-cdk-lib/aws-ecs/test/fargate/fargate-task-definition.test.ts @@ -60,6 +60,7 @@ describe('fargate task definition', () => { cpuArchitecture: ecs.CpuArchitecture.X86_64, operatingSystemFamily: ecs.OperatingSystemFamily.LINUX, }, + pidMode: ecs.PidMode.HOST, }); taskDefinition.addVolume({ @@ -84,6 +85,7 @@ describe('fargate task definition', () => { Family: 'myApp', Memory: '1024', NetworkMode: 'awsvpc', + PidMode: 'host', RequiresCompatibilities: [ ecs.LaunchType.FARGATE, ], @@ -161,6 +163,38 @@ describe('fargate task definition', () => { // THEN }); + + test('throws when pidMode is specified on Windows', () => { + // GIVEN + const stack = new cdk.Stack(); + + // WHEN + // THEN + expect(() => { + new ecs.FargateTaskDefinition(stack, 'FargateTaskDef', { + pidMode: ecs.PidMode.HOST, + runtimePlatform: { + operatingSystemFamily: ecs.OperatingSystemFamily.WINDOWS_SERVER_2019_CORE, + cpuArchitecture: ecs.CpuArchitecture.X86_64, + }, + cpu: 1024, + memoryLimitMiB: 2048, + }); + }).toThrow(/'pidMode' is not supported for Windows containers./); + }); + + test('throws when pidMode is not host', () => { + // GIVEN + const stack = new cdk.Stack(); + + // WHEN + // THEN + expect(() => { + new ecs.FargateTaskDefinition(stack, 'FargateTaskDef', { + pidMode: ecs.PidMode.TASK, + }); + }).toThrow(/'pidMode' can only be set to 'host' for Fargate containers, got: 'task'./); + }); }); describe('When configuredAtLaunch in the Volume', ()=> { test('do not throw when configuredAtLaunch is false', () => { From dd6b0aea86ecae163f5986444fe7061b44da3659 Mon Sep 17 00:00:00 2001 From: Luca Pizzini Date: Sat, 13 Apr 2024 18:22:49 +0200 Subject: [PATCH 2/2] add platform version validation for ephemeralStorageGiB and pidMode --- .../aws-ecs/lib/base/task-definition.ts | 12 ++- .../aws-ecs/lib/fargate/fargate-service.ts | 28 +++--- .../test/fargate/fargate-service.test.ts | 87 +++++++++++++++++++ 3 files changed, 116 insertions(+), 11 deletions(-) diff --git a/packages/aws-cdk-lib/aws-ecs/lib/base/task-definition.ts b/packages/aws-cdk-lib/aws-ecs/lib/base/task-definition.ts index be15cf6c195c5..c079f1aae02a3 100644 --- a/packages/aws-cdk-lib/aws-ecs/lib/base/task-definition.ts +++ b/packages/aws-cdk-lib/aws-ecs/lib/base/task-definition.ts @@ -374,6 +374,15 @@ export class TaskDefinition extends TaskDefinitionBase { */ public readonly ephemeralStorageGiB?: number; + /** + * The process namespace to use for the containers in the task. + * + * Only supported for tasks that are hosted on AWS Fargate if the tasks + * are using platform version 1.4.0 or later (Linux). + * Not supported in Windows containers. + */ + public readonly pidMode?: PidMode; + /** * The container definitions. */ @@ -455,6 +464,7 @@ export class TaskDefinition extends TaskDefinitionBase { } this.ephemeralStorageGiB = props.ephemeralStorageGiB; + this.pidMode = props.pidMode; // validate the cpu and memory size for the Windows operation system family. if (props.runtimePlatform?.operatingSystemFamily?.isWindows()) { @@ -487,7 +497,7 @@ export class TaskDefinition extends TaskDefinitionBase { cpu: props.cpu, memory: props.memoryMiB, ipcMode: props.ipcMode, - pidMode: props.pidMode, + pidMode: this.pidMode, inferenceAccelerators: Lazy.any({ produce: () => !isFargateCompatible(this.compatibility) ? this.renderInferenceAccelerators() : undefined, diff --git a/packages/aws-cdk-lib/aws-ecs/lib/fargate/fargate-service.ts b/packages/aws-cdk-lib/aws-ecs/lib/fargate/fargate-service.ts index fa4194fa4f1aa..b4d1324e18947 100644 --- a/packages/aws-cdk-lib/aws-ecs/lib/fargate/fargate-service.ts +++ b/packages/aws-cdk-lib/aws-ecs/lib/fargate/fargate-service.ts @@ -128,6 +128,23 @@ export class FargateService extends BaseService implements IFargateService { throw new Error('Only one of SecurityGroup or SecurityGroups can be populated.'); } + // Platform versions not supporting referencesSecretJsonField, ephemeralStorageGiB, or pidMode on a task definition + const unsupportedPlatformVersions = [ + FargatePlatformVersion.VERSION1_0, + FargatePlatformVersion.VERSION1_1, + FargatePlatformVersion.VERSION1_2, + FargatePlatformVersion.VERSION1_3, + ]; + const isUnsupportedPlatformVersion = props.platformVersion && unsupportedPlatformVersions.includes(props.platformVersion); + + if (props.taskDefinition.ephemeralStorageGiB && isUnsupportedPlatformVersion) { + throw new Error(`The ephemeralStorageGiB feature requires platform version ${FargatePlatformVersion.VERSION1_4} or later, got ${props.platformVersion}.`); + } + + if (props.taskDefinition.pidMode && isUnsupportedPlatformVersion) { + throw new Error(`The pidMode feature requires platform version ${FargatePlatformVersion.VERSION1_4} or later, got ${props.platformVersion}.`); + } + super(scope, id, { ...props, desiredCount: props.desiredCount, @@ -153,9 +170,7 @@ export class FargateService extends BaseService implements IFargateService { } this.node.addValidation({ - validate: () => this.taskDefinition.referencesSecretJsonField - && props.platformVersion - && SECRET_JSON_FIELD_UNSUPPORTED_PLATFORM_VERSIONS.includes(props.platformVersion) + validate: () => this.taskDefinition.referencesSecretJsonField && isUnsupportedPlatformVersion ? [`The task definition of this service uses at least one container that references a secret JSON field. This feature requires platform version ${FargatePlatformVersion.VERSION1_4} or later.`] : [], }); @@ -214,10 +229,3 @@ export enum FargatePlatformVersion { */ VERSION1_0 = '1.0.0', } - -const SECRET_JSON_FIELD_UNSUPPORTED_PLATFORM_VERSIONS = [ - FargatePlatformVersion.VERSION1_0, - FargatePlatformVersion.VERSION1_1, - FargatePlatformVersion.VERSION1_2, - FargatePlatformVersion.VERSION1_3, -]; diff --git a/packages/aws-cdk-lib/aws-ecs/test/fargate/fargate-service.test.ts b/packages/aws-cdk-lib/aws-ecs/test/fargate/fargate-service.test.ts index 3ef83abd316b3..0ecd027501019 100644 --- a/packages/aws-cdk-lib/aws-ecs/test/fargate/fargate-service.test.ts +++ b/packages/aws-cdk-lib/aws-ecs/test/fargate/fargate-service.test.ts @@ -1,6 +1,7 @@ import { testDeprecated } from '@aws-cdk/cdk-build-tools'; import { Annotations, Match, Template } from '../../../assertions'; import * as appscaling from '../../../aws-applicationautoscaling'; +import * as batch from '../../../aws-batch'; import * as cloudwatch from '../../../aws-cloudwatch'; import * as ec2 from '../../../aws-ec2'; import * as elbv2 from '../../../aws-elasticloadbalancingv2'; @@ -685,6 +686,92 @@ describe('fargate service', () => { }).toThrow(/one essential container/); }); + test('errors when platform version does not support containers which references secret JSON field', () => { + // GIVEN + const stack = new cdk.Stack(); + const vpc = new ec2.Vpc(stack, 'MyVpc', {}); + const cluster = new ecs.Cluster(stack, 'EcsCluster', { vpc }); + const taskDefinition = new ecs.FargateTaskDefinition(stack, 'FargateTaskDef', { + runtimePlatform: { + operatingSystemFamily: ecs.OperatingSystemFamily.LINUX, + cpuArchitecture: ecs.CpuArchitecture.ARM64, + }, + memoryLimitMiB: 512, + cpu: 256, + }); + + // Errors on validation, not on construction. + new ecs.FargateService(stack, 'FargateService', { + cluster, + taskDefinition, + platformVersion: ecs.FargatePlatformVersion.VERSION1_2, + }); + + taskDefinition.addContainer('main', { + image: ecs.ContainerImage.fromRegistry('somecontainer'), + secrets: { + envName: batch.Secret.fromSecretsManager(new secretsmanager.Secret(stack, 'testSecret'), 'secretField'), + }, + }); + + // THEN + expect(() => { + Template.fromStack(stack); + }).toThrow(/This feature requires platform version/); + }); + + test('errors when platform version does not support ephemeralStorageGiB', () => { + // GIVEN + const stack = new cdk.Stack(); + const vpc = new ec2.Vpc(stack, 'MyVpc', {}); + const cluster = new ecs.Cluster(stack, 'EcsCluster', { vpc }); + const taskDefinition = new ecs.FargateTaskDefinition(stack, 'FargateTaskDef', { + runtimePlatform: { + operatingSystemFamily: ecs.OperatingSystemFamily.LINUX, + cpuArchitecture: ecs.CpuArchitecture.ARM64, + }, + memoryLimitMiB: 512, + cpu: 256, + ephemeralStorageGiB: 100, + }); + + // WHEN + // THEN + expect(() => { + new ecs.FargateService(stack, 'FargateService', { + cluster, + taskDefinition, + platformVersion: ecs.FargatePlatformVersion.VERSION1_2, + }); + }).toThrow(/The ephemeralStorageGiB feature requires platform version/); + }); + + test('errors when platform version does not support pidMode', () => { + // GIVEN + const stack = new cdk.Stack(); + const vpc = new ec2.Vpc(stack, 'MyVpc', {}); + const cluster = new ecs.Cluster(stack, 'EcsCluster', { vpc }); + const taskDefinition = new ecs.FargateTaskDefinition(stack, 'FargateTaskDef', { + runtimePlatform: { + operatingSystemFamily: ecs.OperatingSystemFamily.LINUX, + cpuArchitecture: ecs.CpuArchitecture.ARM64, + }, + memoryLimitMiB: 512, + cpu: 256, + pidMode: ecs.PidMode.HOST, + }); + + // WHEN + // THEN + expect(() => { + new ecs.FargateService(stack, 'FargateService', { + cluster, + taskDefinition, + platformVersion: ecs.FargatePlatformVersion.VERSION1_2, + }); + }).toThrow(/The pidMode feature requires platform version/); + }); + test('allows adding the default container after creating the service', () => { // GIVEN const stack = new cdk.Stack();