From 713fadd44ee43bb084d299c9f575b3317b50a130 Mon Sep 17 00:00:00 2001 From: phuhung273 Date: Sun, 22 Dec 2024 02:22:05 +0000 Subject: [PATCH] ECS ExternalService support daemon --- .../aws-ecs/lib/external/external-service.ts | 23 +++++- .../test/external/external-service.test.ts | 79 +++++++++++++++++++ 2 files changed, 100 insertions(+), 2 deletions(-) diff --git a/packages/aws-cdk-lib/aws-ecs/lib/external/external-service.ts b/packages/aws-cdk-lib/aws-ecs/lib/external/external-service.ts index 89b52f908554a..a4ba72f138525 100644 --- a/packages/aws-cdk-lib/aws-ecs/lib/external/external-service.ts +++ b/packages/aws-cdk-lib/aws-ecs/lib/external/external-service.ts @@ -27,6 +27,16 @@ export interface ExternalServiceProps extends BaseServiceOptions { * @default - A new security group is created. */ readonly securityGroups?: ec2.ISecurityGroup[]; + + /** + * Specifies whether the service will use the daemon scheduling strategy. + * If true, the service scheduler deploys exactly one task on each container instance in your cluster. + * + * When you are using this strategy, do not specify a desired number of tasks or any task placement strategies. + * + * @default false + */ + readonly daemon?: boolean; } /** @@ -89,6 +99,14 @@ export class ExternalService extends BaseService implements IExternalService { * Constructs a new instance of the ExternalService class. */ constructor(scope: Construct, id: string, props: ExternalServiceProps) { + if (props.daemon && props.desiredCount !== undefined) { + throw new Error('Daemon mode launches one task on every instance. Don\'t supply desiredCount.'); + } + + if (props.daemon && props.maxHealthyPercent !== undefined && props.maxHealthyPercent !== 100) { + throw new Error('Maximum percent must be 100 for daemon mode.'); + } + if (props.minHealthyPercent !== undefined && props.maxHealthyPercent !== undefined && props.minHealthyPercent >= props.maxHealthyPercent) { throw new Error('Minimum healthy percent must be less than maximum healthy percent.'); } @@ -114,8 +132,8 @@ export class ExternalService extends BaseService implements IExternalService { super(scope, id, { ...props, desiredCount: props.desiredCount, - maxHealthyPercent: props.maxHealthyPercent === undefined ? 100 : props.maxHealthyPercent, - minHealthyPercent: props.minHealthyPercent === undefined ? 0 : props.minHealthyPercent, + maxHealthyPercent: props.daemon || props.maxHealthyPercent === undefined ? 100 : props.maxHealthyPercent, + minHealthyPercent: props.daemon || props.minHealthyPercent === undefined ? 0 : props.minHealthyPercent, launchType: LaunchType.EXTERNAL, propagateTags: propagateTagsFromSource, enableECSManagedTags: props.enableECSManagedTags, @@ -123,6 +141,7 @@ export class ExternalService extends BaseService implements IExternalService { { cluster: props.cluster.clusterName, taskDefinition: props.deploymentController?.type === DeploymentControllerType.EXTERNAL ? undefined : props.taskDefinition.taskDefinitionArn, + schedulingStrategy: props.daemon ? 'DAEMON' : 'REPLICA', }, props.taskDefinition); this.node.addValidation({ diff --git a/packages/aws-cdk-lib/aws-ecs/test/external/external-service.test.ts b/packages/aws-cdk-lib/aws-ecs/test/external/external-service.test.ts index d77246d693959..035e88aa33805 100644 --- a/packages/aws-cdk-lib/aws-ecs/test/external/external-service.test.ts +++ b/packages/aws-cdk-lib/aws-ecs/test/external/external-service.test.ts @@ -291,6 +291,85 @@ describe('external service', () => { }); + test('errors if daemon and desiredCount both specified', () => { + // GIVEN + const stack = new cdk.Stack(); + const vpc = new ec2.Vpc(stack, 'MyVpc', {}); + const cluster = new ecs.Cluster(stack, 'EcsCluster', { vpc }); + addDefaultCapacityProvider(cluster, stack, vpc); + const taskDefinition = new ecs.ExternalTaskDefinition(stack, 'TaskDef'); + taskDefinition.addContainer('BaseContainer', { + image: ecs.ContainerImage.fromRegistry('test'), + memoryReservationMiB: 10, + }); + + // THEN + expect(() => { + new ecs.ExternalService(stack, 'ExternalService', { + cluster, + taskDefinition, + daemon: true, + desiredCount: 2, + }); + }).toThrow(/Don't supply desiredCount/); + + }); + + test('errors if daemon and maximumPercent not 100', () => { + // GIVEN + const stack = new cdk.Stack(); + const vpc = new ec2.Vpc(stack, 'MyVpc', {}); + const cluster = new ecs.Cluster(stack, 'EcsCluster', { vpc }); + addDefaultCapacityProvider(cluster, stack, vpc); + const taskDefinition = new ecs.ExternalTaskDefinition(stack, 'TaskDef'); + taskDefinition.addContainer('BaseContainer', { + image: ecs.ContainerImage.fromRegistry('test'), + memoryReservationMiB: 10, + }); + + // THEN + expect(() => { + new ecs.ExternalService(stack, 'ExternalService', { + cluster, + taskDefinition, + daemon: true, + maxHealthyPercent: 300, + }); + }).toThrow(/Maximum percent must be 100 for daemon mode./); + + }); + + test('sets daemon scheduling strategy', () => { + // GIVEN + const stack = new cdk.Stack(); + const vpc = new ec2.Vpc(stack, 'MyVpc', {}); + const cluster = new ecs.Cluster(stack, 'EcsCluster', { vpc }); + addDefaultCapacityProvider(cluster, stack, vpc); + const taskDefinition = new ecs.ExternalTaskDefinition(stack, 'TaskDef'); + taskDefinition.addContainer('BaseContainer', { + image: ecs.ContainerImage.fromRegistry('test'), + memoryReservationMiB: 10, + }); + + new ecs.ExternalService(stack, 'ExternalService', { + cluster, + taskDefinition, + daemon: true, + }); + + // THEN + Template.fromStack(stack).hasResourceProperties('AWS::ECS::Service', { + SchedulingStrategy: 'DAEMON', + DeploymentConfiguration: { + MaximumPercent: 100, + MinimumHealthyPercent: 0, + }, + EnableECSManagedTags: false, + LaunchType: LaunchType.EXTERNAL, + }); + + }); + test('errors if minimum not less than maximum', () => { // GIVEN const stack = new cdk.Stack();