From 01840f4de9e91d95be191e87ef3a2f81dc12abb8 Mon Sep 17 00:00:00 2001 From: Michael Fischer Date: Mon, 26 Apr 2021 18:44:20 -0700 Subject: [PATCH 1/9] feat(ecs): add support for EC2 Capacity Providers --- packages/@aws-cdk/aws-ecs/README.md | 58 ++-- packages/@aws-cdk/aws-ecs/lib/cluster.ts | 292 ++++++++++++++++-- .../@aws-cdk/aws-ecs/test/cluster.test.ts | 179 ++++++++++- .../aws-ecs/test/ec2/ec2-service.test.ts | 53 ++++ .../test/fargate/fargate-service.test.ts | 89 +++++- 5 files changed, 615 insertions(+), 56 deletions(-) diff --git a/packages/@aws-cdk/aws-ecs/README.md b/packages/@aws-cdk/aws-ecs/README.md index 7f0338ed3ff18..3ce420ed210ec 100644 --- a/packages/@aws-cdk/aws-ecs/README.md +++ b/packages/@aws-cdk/aws-ecs/README.md @@ -14,16 +14,13 @@ This package contains constructs for working with **Amazon Elastic Container Service** (Amazon ECS). -Amazon ECS is a highly scalable, fast, container management service -that makes it easy to run, stop, -and manage Docker containers on a cluster of Amazon EC2 instances. +Amazon Elastic Container Service (Amazon ECS) is a fully managed container orchestration service. For further information on Amazon ECS, see the [Amazon ECS documentation](https://docs.aws.amazon.com/ecs) -The following example creates an Amazon ECS cluster, -adds capacity to it, -and instantiates the Amazon ECS Service with an automatic load balancer. +The following example creates an Amazon ECS cluster, adds capacity to it, and +runs a service on it: ```ts import * as ecs from '@aws-cdk/aws-ecs'; @@ -496,38 +493,39 @@ scaling.scaleOnRequestCount('RequestScaling', { Task auto-scaling is powered by *Application Auto-Scaling*. See that section for details. -## Instance Auto-Scaling +## Managed Scaling for Amazon EC2 Capacity -If you're running on AWS Fargate, AWS manages the physical machines that your -containers are running on for you. If you're running an Amazon ECS cluster however, -your Amazon EC2 instances might fill up as your number of Tasks goes up. - -To avoid placement errors, configure auto-scaling for your -Amazon EC2 instance group so that your instance count scales with demand. To keep -your Amazon EC2 instances halfway loaded, scaling up to a maximum of 30 instances -if required: +Amazon ECS can manage EC2 capacity on which you can place your tasks. You can +enable this by using an EC2 Capacity Provider and associating it with you ECS +tasks and services. If enabled, ECS will scale out the Auto Scaling Group(s) as +necessary to fit your tasks. ```ts -const autoScalingGroup = cluster.addCapacity('DefaultAutoScalingGroup', { - instanceType: new ec2.InstanceType("t2.xlarge"), - minCapacity: 3, - maxCapacity: 30, - desiredCapacity: 3, - - // Give instances 5 minutes to drain running tasks when an instance is - // terminated. This is the default, turn this off by specifying 0 or - // change the timeout up to 900 seconds. - taskDrainTime: Duration.seconds(300) +const autoScalingGroup = new autoscaling.AutoScalingGroup(this, 'Compute', { + vpc, + instanceType: new ec2.InstanceType('t2.xlarge'), + minCapacity: 0, + maxCapacity: 100 }); +const capacity = new ecs.CapacityProvider(this, 'EC2Capacity', { + autoScalingGroup +}); +cluster.addCapacityProvider(capacity); -autoScalingGroup.scaleOnCpuUtilization('KeepCpuHalfwayLoaded', { - targetUtilizationPercent: 50 +const service = new ecs.Ec2Service(this, 'Service', { + cluster, + taskDefinition, + desiredCount: 10, + capacityProviderStrategies: [ + { + base: 0, + weight: 1, + capacityProvider: capacity.capacityProviderName + } + ] }); ``` -See the `@aws-cdk/aws-autoscaling` library for more autoscaling options -you can configure on your instances. - ## Integration with CloudWatch Events To start an Amazon ECS task on an Amazon EC2-backed Cluster, instantiate an diff --git a/packages/@aws-cdk/aws-ecs/lib/cluster.ts b/packages/@aws-cdk/aws-ecs/lib/cluster.ts index a65efaa83e17e..b69e0f941c380 100644 --- a/packages/@aws-cdk/aws-ecs/lib/cluster.ts +++ b/packages/@aws-cdk/aws-ecs/lib/cluster.ts @@ -5,11 +5,11 @@ import * as iam from '@aws-cdk/aws-iam'; import * as kms from '@aws-cdk/aws-kms'; import * as cloudmap from '@aws-cdk/aws-servicediscovery'; import * as ssm from '@aws-cdk/aws-ssm'; -import { Duration, Lazy, IResource, Resource, Stack } from '@aws-cdk/core'; +import { Duration, Lazy, IResource, Resource, Stack, Aspects, IAspect, IConstruct } from '@aws-cdk/core'; import { Construct } from 'constructs'; import { InstanceDrainHook } from './drain-hook/instance-drain-hook'; import { ECSMetrics } from './ecs-canned-metrics.generated'; -import { CfnCluster } from './ecs.generated'; +import { CfnCluster, CfnCapacityProvider, CfnClusterCapacityProviderAssociations } from './ecs.generated'; // v2 - keep this import as a separate section to reduce merge conflict when forward merging with the v2 branch. // eslint-disable-next-line @@ -52,9 +52,24 @@ export interface ClusterProps { * The capacity providers to add to the cluster * * @default - None. Currently only FARGATE and FARGATE_SPOT are supported. + * @deprecated Use {@link ClusterProps.enableFargateCapacityProvider} and {@link ClusterProps.enableFargateSpotCapacityProvider} instead. */ readonly capacityProviders?: string[]; + /** + * Whether to enable Fargate Capacity Provider + * + * @default false + */ + readonly enableFargateCapacityProvider?: boolean; + + /** + * Whether to enable Fargate Spot Capacity Provider + * + * @default false + */ + readonly enableFargateSpotCapacityProvider?: boolean; + /** * If true CloudWatch Container Insights will be enabled for the cluster * @@ -110,10 +125,8 @@ export class Cluster extends Resource implements ICluster { /** * The capacity providers associated with the cluster. - * - * Currently only FARGATE and FARGATE_SPOT are supported. */ - private _capacityProviders: string[] = []; + private _capacityProviders: ICapacityProvider[] = []; /** * The AWS Cloud Map namespace to associate with the cluster. @@ -148,12 +161,22 @@ export class Cluster extends Resource implements ICluster { clusterSettings = [{ name: 'containerInsights', value: props.containerInsights ? ContainerInsights.ENABLED : ContainerInsights.DISABLED }]; } - this._capacityProviders = props.capacityProviders ?? []; + if (props.enableFargateCapacityProvider || props.capacityProviders?.includes('FARGATE')) { + this.enableFargateCapacityProvider(); + } + if (props.enableFargateSpotCapacityProvider || props.capacityProviders?.includes('FARGATE_SPOT')) { + this.enableFargateSpotCapacityProvider(); + } const cluster = new CfnCluster(this, 'Resource', { clusterName: this.physicalName, clusterSettings, - capacityProviders: Lazy.list({ produce: () => this._capacityProviders }, { omitEmpty: true }), + capacityProviders: Lazy.list( + { + produce: () => this._capacityProviders.map(p => p.capacityProviderName).filter(p => p === 'FARGATE' || p === 'FARGATE_SPOT'), + }, + { omitEmpty: true }, + ), }); this.clusterArn = this.getResourceArnAttribute(cluster.attrArn, { @@ -173,6 +196,31 @@ export class Cluster extends Resource implements ICluster { this._autoscalingGroup = props.capacity !== undefined ? this.addCapacity('DefaultAutoScalingGroup', props.capacity) : undefined; + + // Only create cluster capacity provider associations if there are any EC2 + // capacity providers. Ordinarily we'd just add the construct to the tree + // since it's harmless, but we'd prefer not to add unexpected new + // resources to the stack which could surprise users working with + // brown-field CDK apps and stacks. + Aspects.of(this).add(new MaybeCreateCapacityProviderAssociations(this, id, this._capacityProviders)); + } + + /** + * Enable the Fargate capacity provider for this cluster. + */ + public enableFargateCapacityProvider() { + if (!this._capacityProviders.includes(FargateCapacityProvider)) { + this._capacityProviders.push(FargateCapacityProvider); + } + } + + /** + * Enable the Fargate Spot capacity provider for this cluster. + */ + public enableFargateSpotCapacityProvider() { + if (!this._capacityProviders.includes(FargateSpotCapacityProvider)) { + this._capacityProviders.push(FargateSpotCapacityProvider); + } } /** @@ -214,6 +262,8 @@ export class Cluster extends Resource implements ICluster { * This method adds compute capacity to a cluster by creating an AutoScalingGroup with the specified options. * * Returns the AutoScalingGroup so you can add autoscaling settings to it. + * + * @deprecated Use {@link Cluster.addCapacityProvider} instead. */ public addCapacity(id: string, options: AddCapacityOptions): autoscaling.AutoScalingGroup { if (options.machineImage && options.machineImageType) { @@ -238,9 +288,46 @@ export class Cluster extends Resource implements ICluster { return autoScalingGroup; } + /** + * This method adds compute capacity to a cluster using the specified EC2 Capacity Provider. + * + * @param provider the capacity provider to add to this cluster. + */ + public addCapacityProvider(provider: ICapacityProvider | string, options: AddAutoScalingGroupCapacityOptions = {}) { + // Backward compatibility + if (typeof(provider) === 'string') { + switch (provider) { + case 'FARGATE': + this.addCapacityProvider(FargateCapacityProvider); + return; + case 'FARGATE_SPOT': + this.addCapacityProvider(FargateSpotCapacityProvider); + return; + default: + throw new Error('CapacityProvider not supported'); + } + } + // Don't add the same provider twice + if (this._capacityProviders.includes(provider)) { + return; + } + + if (provider instanceof EC2CapacityProvider) { + this._hasEc2Capacity = true; + this.configureAutoScalingGroup(provider.autoScalingGroup, { + ...options, + // Don't enable the instance-draining lifecycle hook if managed termination protection is enabled + taskDrainTime: provider.enableManagedTerminationProtection ? Duration.seconds(0) : options.taskDrainTime, + }); + } + + this._capacityProviders.push(provider); + } + /** * This method adds compute capacity to a cluster using the specified AutoScalingGroup. * + * @deprecated Use {@link Cluster.addCapacityProvider} instead. * @param autoScalingGroup the ASG to add to this cluster. * [disable-awslint:ref-via-interface] is needed in order to install the ECS * agent by updating the ASGs user data. @@ -248,8 +335,11 @@ export class Cluster extends Resource implements ICluster { public addAutoScalingGroup(autoScalingGroup: autoscaling.AutoScalingGroup, options: AddAutoScalingGroupCapacityOptions = {}) { this._hasEc2Capacity = true; this.connections.connections.addSecurityGroup(...autoScalingGroup.connections.securityGroups); + this.configureAutoScalingGroup(autoScalingGroup, options); + } - if ( autoScalingGroup.osType === ec2.OperatingSystemType.WINDOWS ) { + private configureAutoScalingGroup(autoScalingGroup: autoscaling.AutoScalingGroup, options: AddAutoScalingGroupCapacityOptions = {}) { + if (autoScalingGroup.osType === ec2.OperatingSystemType.WINDOWS) { this.configureWindowsAutoScalingGroup(autoScalingGroup, options); } else { // Tie instances to cluster @@ -341,21 +431,6 @@ export class Cluster extends Resource implements ICluster { } } - /** - * addCapacityProvider adds the name of a capacityProvider to the list of supproted capacityProviders for a cluster. - * - * @param provider the capacity provider to add to this cluster. - */ - public addCapacityProvider(provider: string) { - if (!(provider === 'FARGATE' || provider === 'FARGATE_SPOT')) { - throw new Error('CapacityProvider not supported'); - } - - if (!this._capacityProviders.includes(provider)) { - this._capacityProviders.push(provider); - } - } - private configureWindowsAutoScalingGroup(autoScalingGroup: autoscaling.AutoScalingGroup, options: AddAutoScalingGroupCapacityOptions = {}) { // clear the cache of the agent autoScalingGroup.addUserData('Remove-Item -Recurse C:\\ProgramData\\Amazon\\ECS\\Cache'); @@ -859,6 +934,7 @@ export interface AddAutoScalingGroupCapacityOptions { * * Set to 0 to disable task draining. * + * @deprecated The lifecycle draining hook is not configured if using the EC2 Capacity Provider. Enable managed termination protection instead. * @default Duration.minutes(5) */ readonly taskDrainTime?: Duration; @@ -975,7 +1051,7 @@ enum ContainerInsights { */ export interface CapacityProviderStrategy { /** - * The name of the Capacity Provider. Currently only FARGATE and FARGATE_SPOT are supported. + * The capacity provider */ readonly capacityProvider: string; @@ -997,3 +1073,171 @@ capacity provider. The weight value is taken into consideration after the base v */ readonly weight?: number; } + +/** + * An ECS Capacity Provider + */ +export interface ICapacityProvider { + /** + * The name for the capacity provider. + * + * @default CloudFormation-generated name + */ + readonly capacityProviderName: string; +} + +/** + * A class used solely for generating the singleton objects that follow + */ +class NamedCapacityProvider implements ICapacityProvider { + public capacityProviderName: string; + constructor(name: string) { + this.capacityProviderName = name; + } +} + +/** + * Fargate capacity provider + */ +export const FargateCapacityProvider = new NamedCapacityProvider('FARGATE'); + +/** + * Fargate Spot capacity provider + */ +export const FargateSpotCapacityProvider = new NamedCapacityProvider('FARGATE_SPOT'); + +/** + * The options for creating an EC2 Capacity Provider. + */ +export interface EC2CapacityProviderProps extends AddAutoScalingGroupCapacityOptions { + /** + * The name for the capacity provider. + * + * @default CloudFormation-generated name + */ + readonly capacityProviderName?: string; + + /** + * The autoscaling group to add as a Capacity Provider. + * [disable-awslint:ref-via-interface] is needed to enable managed termination + * protection. + */ + readonly autoScalingGroup: autoscaling.AutoScalingGroup; + + /** + * Whether to enable managed scaling + * + * @default true + */ + readonly enableManagedScaling?: boolean; + + /** + * Whether to enable managed termination protection + * + * @default true + */ + readonly enableManagedTerminationProtection?: boolean; + + /** + * Maximum scaling step size. In most cases this should be left alone. + * + * @default 1000 + */ + readonly maximumScalingStepSize?: number; + + /** + * Minimum scaling step size. In most cases this should be left alone. + * + * @default 1 + */ + readonly minimumScalingStepSize?: number; + + /** + * Target capacity percent. In most cases this should be left alone. + * + * @default 100 + */ + readonly targetCapacityPercent?: number; +} + +/** + * An EC2 Capacity Provider. This allows an ECS cluster to target a specific + * EC2 Auto Scaling Group for the placement of tasks. Optionally (and recommended), + * ECS can manage the number of instances in the ASG to fit the tasks, and can + * ensure that instances are not prematurely terminated while there are still tasks + * running on them. + * [disable-awslint:ref-via-interface] is needed to enable managed termination + * protection. + */ +export class EC2CapacityProvider extends CoreConstruct implements ICapacityProvider { + /** + * Capacity provider name + * @default Chosen by CloudFormation + */ + readonly capacityProviderName: string; + + /** + * Auto Scaling Group + */ + readonly autoScalingGroup: autoscaling.AutoScalingGroup; + + /** + * Whether managed termination protection is enabled + */ + readonly enableManagedTerminationProtection?: boolean; + + constructor(scope: Construct, id: string, props: EC2CapacityProviderProps) { + super(scope, id); + + this.autoScalingGroup = props.autoScalingGroup; + this.enableManagedTerminationProtection = props.enableManagedTerminationProtection; + + if (this.enableManagedTerminationProtection) { + this.autoScalingGroup.protectNewInstancesFromScaleIn(); + } + + const capacityProvider = new CfnCapacityProvider(this, id, { + name: props.capacityProviderName, + autoScalingGroupProvider: { + autoScalingGroupArn: this.autoScalingGroup.autoScalingGroupName, + managedScaling: { + status: (props.enableManagedScaling === undefined || props.enableManagedScaling) ? 'ENABLED' : 'DISABLED', + targetCapacity: props.targetCapacityPercent || 100, + maximumScalingStepSize: props.maximumScalingStepSize, + minimumScalingStepSize: props.minimumScalingStepSize, + }, + managedTerminationProtection: (props.enableManagedTerminationProtection === undefined || props.enableManagedTerminationProtection) ? 'ENABLED' : 'DISABLED', + }, + }); + + this.capacityProviderName = capacityProvider.ref; + } +} + +/** + * A visitor that adds a capacity provider association to a Cluster only if + * the caller created any EC2 Capacity Providers. + */ +class MaybeCreateCapacityProviderAssociations implements IAspect { + private scope: CoreConstruct; + private id: string; + private capacityProviders: ICapacityProvider[] + + constructor(scope: CoreConstruct, id: string, capacityProviders: ICapacityProvider[]) { + this.scope = scope; + this.id = id; + this.capacityProviders = capacityProviders; + } + public visit(node: IConstruct): void { + if (node instanceof Cluster) { + const providers = this.capacityProviders.map(p => p.capacityProviderName).filter(p => p !== 'FARGATE' && p !== 'FARGATE_SPOT'); + if (providers.length > 0) { + new CfnClusterCapacityProviderAssociations(this.scope, this.id, { + cluster: node.clusterName, + defaultCapacityProviderStrategy: [], + capacityProviders: Lazy.list({ produce: () => providers }), + }); + } + } + } +} diff --git a/packages/@aws-cdk/aws-ecs/test/cluster.test.ts b/packages/@aws-cdk/aws-ecs/test/cluster.test.ts index 713338284e57b..c1a48608c049c 100644 --- a/packages/@aws-cdk/aws-ecs/test/cluster.test.ts +++ b/packages/@aws-cdk/aws-ecs/test/cluster.test.ts @@ -5,6 +5,7 @@ import { haveResourceLike, ResourcePart, } from '@aws-cdk/assert-internal'; +import * as autoscaling from '@aws-cdk/aws-autoscaling'; import * as ec2 from '@aws-cdk/aws-ec2'; import * as kms from '@aws-cdk/aws-kms'; import * as cloudmap from '@aws-cdk/aws-servicediscovery'; @@ -1695,7 +1696,7 @@ nodeunitShim({ test.done(); }, - 'allows specifying capacityProviders'(test: Test) { + 'allows specifying capacityProviders (deprecated)'(test: Test) { // GIVEN const app = new cdk.App(); const stack = new cdk.Stack(app, 'test'); @@ -1711,6 +1712,60 @@ nodeunitShim({ test.done(); }, + 'allows specifying capacityProviders'(test: Test) { + // GIVEN + const app = new cdk.App(); + const stack = new cdk.Stack(app, 'test'); + + // WHEN + new ecs.Cluster(stack, 'EcsCluster', { + enableFargateSpotCapacityProvider: true, + }); + + // THEN + expect(stack).to(haveResource('AWS::ECS::Cluster', { + CapacityProviders: ['FARGATE_SPOT'], + })); + + test.done(); + }, + + 'allows specifying capacityProviders (alternate method)'(test: Test) { + // GIVEN + const app = new cdk.App(); + const stack = new cdk.Stack(app, 'test'); + + // WHEN + const cluster = new ecs.Cluster(stack, 'EcsCluster'); + cluster.enableFargateCapacityProvider(); + cluster.enableFargateSpotCapacityProvider(); + + // THEN + expect(stack).to(haveResource('AWS::ECS::Cluster', { + CapacityProviders: ['FARGATE', 'FARGATE_SPOT'], + })); + + test.done(); + }, + + 'allows adding capacityProviders post-construction (deprecated)'(test: Test) { + // GIVEN + const app = new cdk.App(); + const stack = new cdk.Stack(app, 'test'); + const cluster = new ecs.Cluster(stack, 'EcsCluster'); + + // WHEN + cluster.addCapacityProvider(ecs.FargateCapacityProvider); + cluster.addCapacityProvider(ecs.FargateCapacityProvider); // does not add twice + + // THEN + expect(stack).to(haveResource('AWS::ECS::Cluster', { + CapacityProviders: ['FARGATE'], + })); + + test.done(); + }, + 'allows adding capacityProviders post-construction'(test: Test) { // GIVEN const app = new cdk.App(); @@ -1742,4 +1797,126 @@ nodeunitShim({ test.done(); }, + + 'creates capacity providers with expected defaults'(test: Test) { + // GIVEN + const app = new cdk.App(); + const stack = new cdk.Stack(app, 'test'); + const vpc = new ec2.Vpc(stack, 'Vpc'); + const autoScalingGroup = new autoscaling.AutoScalingGroup(stack, 'asg', { + vpc, + instanceType: new ec2.InstanceType('bogus'), + machineImage: ecs.EcsOptimizedImage.amazonLinux2(), + }); + + // WHEN + new ecs.EC2CapacityProvider(stack, 'provider', { + autoScalingGroup, + }); + + // THEN + expect(stack).to(haveResource('AWS::ECS::CapacityProvider', { + AutoScalingGroupProvider: { + AutoScalingGroupArn: { + Ref: 'asgASG4D014670', + }, + ManagedScaling: { + Status: 'ENABLED', + TargetCapacity: 100, + }, + ManagedTerminationProtection: 'ENABLED', + }, + })); + test.done(); + }, + + 'capacity provider enables ASG new instance scale-in protection'(test: Test) { + // GIVEN + const app = new cdk.App(); + const stack = new cdk.Stack(app, 'test'); + const vpc = new ec2.Vpc(stack, 'Vpc'); + const autoScalingGroup = new autoscaling.AutoScalingGroup(stack, 'asg', { + vpc, + instanceType: new ec2.InstanceType('bogus'), + machineImage: ecs.EcsOptimizedImage.amazonLinux2(), + }); + + // WHEN + new ecs.EC2CapacityProvider(stack, 'provider', { + autoScalingGroup, + enableManagedTerminationProtection: true, + }); + + // THEN + expect(stack).to(haveResource('AWS::AutoScaling::AutoScalingGroup', { + NewInstancesProtectedFromScaleIn: true, + })); + test.done(); + }, + + 'capacity provider disables ASG new instance scale-in protection'(test: Test) { + // GIVEN + const app = new cdk.App(); + const stack = new cdk.Stack(app, 'test'); + const vpc = new ec2.Vpc(stack, 'Vpc'); + const autoScalingGroup = new autoscaling.AutoScalingGroup(stack, 'asg', { + vpc, + instanceType: new ec2.InstanceType('bogus'), + machineImage: ecs.EcsOptimizedImage.amazonLinux2(), + }); + + // WHEN + new ecs.EC2CapacityProvider(stack, 'provider', { + autoScalingGroup, + enableManagedTerminationProtection: false, + }); + + // THEN + expect(stack).notTo(haveResource('AWS::AutoScaling::AutoScalingGroup', { + NewInstancesProtectedFromScaleIn: true, + })); + test.done(); + }, + + 'can add EC2 capacity via Capacity Provider'(test: Test) { + // GIVEN + const app = new cdk.App(); + const stack = new cdk.Stack(app, 'test'); + const vpc = new ec2.Vpc(stack, 'Vpc'); + const cluster = new ecs.Cluster(stack, 'EcsCluster'); + + const autoScalingGroup = new autoscaling.AutoScalingGroup(stack, 'asg', { + vpc, + instanceType: new ec2.InstanceType('bogus'), + machineImage: ecs.EcsOptimizedImage.amazonLinux2(), + }); + + // WHEN + const capacityProvider = new ecs.EC2CapacityProvider(stack, 'provider', { + autoScalingGroup, + enableManagedTerminationProtection: false, + }); + + // These should not be added at the association level + cluster.enableFargateCapacityProvider(); + cluster.enableFargateSpotCapacityProvider(); + + // Ensure not added twice + cluster.addCapacityProvider(capacityProvider); + cluster.addCapacityProvider(capacityProvider); + + // THEN + expect(stack).to(haveResource('AWS::ECS::ClusterCapacityProviderAssociations', { + Cluster: { + Ref: 'EcsCluster97242B84', + }, + CapacityProviders: [ + { + Ref: 'providerD3FF4D3A', + }, + ], + DefaultCapacityProviderStrategy: [], + })); + test.done(); + }, }); diff --git a/packages/@aws-cdk/aws-ecs/test/ec2/ec2-service.test.ts b/packages/@aws-cdk/aws-ecs/test/ec2/ec2-service.test.ts index f6d6a47efc6c3..cac4165948214 100644 --- a/packages/@aws-cdk/aws-ecs/test/ec2/ec2-service.test.ts +++ b/packages/@aws-cdk/aws-ecs/test/ec2/ec2-service.test.ts @@ -1,4 +1,5 @@ import { expect, haveResource, haveResourceLike } from '@aws-cdk/assert-internal'; +import * as autoscaling from '@aws-cdk/aws-autoscaling'; import * as ec2 from '@aws-cdk/aws-ec2'; import * as elb from '@aws-cdk/aws-elasticloadbalancing'; import * as elbv2 from '@aws-cdk/aws-elasticloadbalancingv2'; @@ -236,6 +237,58 @@ nodeunitShim({ test.done(); }, + 'with capacity provider'(test: Test) { + // GIVEN + const stack = new cdk.Stack(); + const vpc = new ec2.Vpc(stack, 'Vpc'); + const cluster = new ecs.Cluster(stack, 'EcsCluster'); + + const autoScalingGroup = new autoscaling.AutoScalingGroup(stack, 'asg', { + vpc, + instanceType: new ec2.InstanceType('bogus'), + machineImage: ecs.EcsOptimizedImage.amazonLinux2(), + }); + + // WHEN + const capacityProvider = new ecs.EC2CapacityProvider(stack, 'provider', { + autoScalingGroup, + enableManagedTerminationProtection: false, + }); + cluster.addCapacityProvider(capacityProvider); + + const taskDefinition = new ecs.TaskDefinition(stack, 'ServerTask', { + compatibility: ecs.Compatibility.EC2, + }); + taskDefinition.addContainer('app', { + image: new ecs.RepositoryImage('bogus'), + cpu: 1024, + memoryReservationMiB: 900, + portMappings: [{ + containerPort: 80, + }], + }); + new ecs.Ec2Service(stack, 'Service', { + cluster, + taskDefinition, + desiredCount: 0, + capacityProviderStrategies: [{ + capacityProvider: capacityProvider.capacityProviderName, + }], + }); + + // THEN + expect(stack).to(haveResource('AWS::ECS::Service', { + CapacityProviderStrategy: [ + { + CapacityProvider: { + Ref: 'providerD3FF4D3A', + }, + }, + ], + })); + test.done(); + }, + 'with multiple security groups, it correctly updates the cfn template'(test: Test) { // GIVEN const stack = new cdk.Stack(); diff --git a/packages/@aws-cdk/aws-ecs/test/fargate/fargate-service.test.ts b/packages/@aws-cdk/aws-ecs/test/fargate/fargate-service.test.ts index 0bdbeac0c04d5..f720ca8d5ae00 100644 --- a/packages/@aws-cdk/aws-ecs/test/fargate/fargate-service.test.ts +++ b/packages/@aws-cdk/aws-ecs/test/fargate/fargate-service.test.ts @@ -110,7 +110,7 @@ nodeunitShim({ test.done(); }, - 'does not set launchType when capacity provider strategies specified'(test: Test) { + 'does not set launchType when capacity provider strategies specified (deprecated)'(test: Test) { // GIVEN const stack = new cdk.Stack(); const vpc = new ec2.Vpc(stack, 'MyVpc', {}); @@ -195,6 +195,93 @@ nodeunitShim({ test.done(); }, + 'does not set launchType when capacity provider strategies specified'(test: Test) { + // GIVEN + const stack = new cdk.Stack(); + const vpc = new ec2.Vpc(stack, 'MyVpc', {}); + const cluster = new ecs.Cluster(stack, 'EcsCluster', { + vpc, + }); + cluster.enableFargateCapacityProvider(); + cluster.enableFargateSpotCapacityProvider(); + + const taskDefinition = new ecs.FargateTaskDefinition(stack, 'FargateTaskDef'); + + const container = taskDefinition.addContainer('web', { + image: ecs.ContainerImage.fromRegistry('amazon/amazon-ecs-sample'), + memoryLimitMiB: 512, + }); + container.addPortMappings({ containerPort: 8000 }); + + new ecs.FargateService(stack, 'FargateService', { + cluster, + taskDefinition, + capacityProviderStrategies: [ + { + capacityProvider: 'FARGATE_SPOT', + weight: 2, + }, + { + capacityProvider: 'FARGATE', + weight: 1, + }, + ], + }); + + // THEN + expect(stack).to(haveResource('AWS::ECS::Cluster', { + CapacityProviders: ['FARGATE', 'FARGATE_SPOT'], + })); + + expect(stack).to(haveResource('AWS::ECS::Service', { + TaskDefinition: { + Ref: 'FargateTaskDefC6FB60B4', + }, + Cluster: { + Ref: 'EcsCluster97242B84', + }, + DeploymentConfiguration: { + MaximumPercent: 200, + MinimumHealthyPercent: 50, + }, + // no launch type + CapacityProviderStrategy: [ + { + CapacityProvider: 'FARGATE_SPOT', + Weight: 2, + }, + { + CapacityProvider: 'FARGATE', + Weight: 1, + }, + ], + EnableECSManagedTags: false, + NetworkConfiguration: { + AwsvpcConfiguration: { + AssignPublicIp: 'DISABLED', + SecurityGroups: [ + { + 'Fn::GetAtt': [ + 'FargateServiceSecurityGroup0A0E79CB', + 'GroupId', + ], + }, + ], + Subnets: [ + { + Ref: 'MyVpcPrivateSubnet1Subnet5057CF7E', + }, + { + Ref: 'MyVpcPrivateSubnet2Subnet0040C983', + }, + ], + }, + }, + })); + + test.done(); + }, + 'with custom cloudmap namespace'(test: Test) { // GIVEN const stack = new cdk.Stack(); From a31c44113756936462472be04f3bbc73f13cd1f2 Mon Sep 17 00:00:00 2001 From: "Michael S. Fischer" Date: Mon, 10 May 2021 11:22:32 -0700 Subject: [PATCH 2/9] Apply suggestions from code review Co-authored-by: Hsing-Hui Hsu --- packages/@aws-cdk/aws-ecs/lib/cluster.ts | 2 +- packages/@aws-cdk/aws-ecs/test/cluster.test.ts | 2 +- packages/@aws-cdk/aws-ecs/test/ec2/ec2-service.test.ts | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/packages/@aws-cdk/aws-ecs/lib/cluster.ts b/packages/@aws-cdk/aws-ecs/lib/cluster.ts index b69e0f941c380..625b0935aca29 100644 --- a/packages/@aws-cdk/aws-ecs/lib/cluster.ts +++ b/packages/@aws-cdk/aws-ecs/lib/cluster.ts @@ -1051,7 +1051,7 @@ enum ContainerInsights { */ export interface CapacityProviderStrategy { /** - * The capacity provider + * The name of the capacity provider */ readonly capacityProvider: string; diff --git a/packages/@aws-cdk/aws-ecs/test/cluster.test.ts b/packages/@aws-cdk/aws-ecs/test/cluster.test.ts index c1a48608c049c..597edfecc3158 100644 --- a/packages/@aws-cdk/aws-ecs/test/cluster.test.ts +++ b/packages/@aws-cdk/aws-ecs/test/cluster.test.ts @@ -1712,7 +1712,7 @@ nodeunitShim({ test.done(); }, - 'allows specifying capacityProviders'(test: Test) { + 'allows specifying Fargate capacityProviders'(test: Test) { // GIVEN const app = new cdk.App(); const stack = new cdk.Stack(app, 'test'); diff --git a/packages/@aws-cdk/aws-ecs/test/ec2/ec2-service.test.ts b/packages/@aws-cdk/aws-ecs/test/ec2/ec2-service.test.ts index cac4165948214..3798a2bd502f0 100644 --- a/packages/@aws-cdk/aws-ecs/test/ec2/ec2-service.test.ts +++ b/packages/@aws-cdk/aws-ecs/test/ec2/ec2-service.test.ts @@ -237,7 +237,7 @@ nodeunitShim({ test.done(); }, - 'with capacity provider'(test: Test) { + 'with autoscaling group capacity provider'(test: Test) { // GIVEN const stack = new cdk.Stack(); const vpc = new ec2.Vpc(stack, 'Vpc'); From e7fc0bfc24725c347f051c6c16bb3d54b2cece61 Mon Sep 17 00:00:00 2001 From: Michael Fischer Date: Mon, 10 May 2021 20:56:17 -0700 Subject: [PATCH 3/9] Update per reviewer feedback --- packages/@aws-cdk/aws-ecs/README.md | 106 +-- packages/@aws-cdk/aws-ecs/lib/cluster.ts | 161 ++--- .../@aws-cdk/aws-ecs/test/cluster.test.ts | 54 +- .../aws-ecs/test/ec2/ec2-service.test.ts | 2 +- .../ec2/integ.capacity-provider.expected.json | 655 ++++++++++++++++++ .../test/ec2/integ.capacity-provider.ts | 46 ++ .../test/fargate/fargate-service.test.ts | 3 +- 7 files changed, 859 insertions(+), 168 deletions(-) create mode 100644 packages/@aws-cdk/aws-ecs/test/ec2/integ.capacity-provider.expected.json create mode 100644 packages/@aws-cdk/aws-ecs/test/ec2/integ.capacity-provider.ts diff --git a/packages/@aws-cdk/aws-ecs/README.md b/packages/@aws-cdk/aws-ecs/README.md index 3ce420ed210ec..f94050e300602 100644 --- a/packages/@aws-cdk/aws-ecs/README.md +++ b/packages/@aws-cdk/aws-ecs/README.md @@ -493,39 +493,6 @@ scaling.scaleOnRequestCount('RequestScaling', { Task auto-scaling is powered by *Application Auto-Scaling*. See that section for details. -## Managed Scaling for Amazon EC2 Capacity - -Amazon ECS can manage EC2 capacity on which you can place your tasks. You can -enable this by using an EC2 Capacity Provider and associating it with you ECS -tasks and services. If enabled, ECS will scale out the Auto Scaling Group(s) as -necessary to fit your tasks. - -```ts -const autoScalingGroup = new autoscaling.AutoScalingGroup(this, 'Compute', { - vpc, - instanceType: new ec2.InstanceType('t2.xlarge'), - minCapacity: 0, - maxCapacity: 100 -}); -const capacity = new ecs.CapacityProvider(this, 'EC2Capacity', { - autoScalingGroup -}); -cluster.addCapacityProvider(capacity); - -const service = new ecs.Ec2Service(this, 'Service', { - cluster, - taskDefinition, - desiredCount: 10, - capacityProviderStrategies: [ - { - base: 0, - weight: 1, - capacityProvider: capacity.capacityProviderName - } - ] -}); -``` - ## Integration with CloudWatch Events To start an Amazon ECS task on an Amazon EC2-backed Cluster, instantiate an @@ -758,25 +725,20 @@ ecsService.associateCloudMapService({ ## Capacity Providers -Currently, only `FARGATE` and `FARGATE_SPOT` capacity providers are supported. +There are two major families of Capacity Providers: Fargate and EC2 (via Auto +Scaling Groups. Both are supported. -To enable capacity providers on your cluster, set the `capacityProviders` field -to [`FARGATE`, `FARGATE_SPOT`]. Then, specify capacity provider strategies on -the `capacityProviderStrategies` field for your Fargate Service. +### Fargate Capacity Providers -```ts -import * as cdk from '@aws-cdk/core'; -import * as ec2 from '@aws-cdk/aws-ec2'; -import * as ecs from '../../lib'; - -const app = new cdk.App(); -const stack = new cdk.Stack(app, 'aws-ecs-integ-capacity-provider'); - -const vpc = new ec2.Vpc(stack, 'Vpc', { maxAzs: 2 }); +To enable Fargate capacity providers, you can either set +`enableFargateCapacityProviders` to `true` when creating your cluster, or by +invoking the `enableFargateCapacityProviders()` method after creating your +cluster. +```ts const cluster = new ecs.Cluster(stack, 'FargateCPCluster', { vpc, - capacityProviders: ['FARGATE', 'FARGATE_SPOT'], + enableFargateCapacityProviders: true, }); const taskDefinition = new ecs.FargateTaskDefinition(stack, 'TaskDef'); @@ -799,8 +761,56 @@ new ecs.FargateService(stack, 'FargateService', { } ], }); +``` + +### EC2 Capacity Providers + +To add an EC2 Capacity Provider, first create an EC2 Auto Scaling Group. Then, +create an `EC2CapacityProvider` and pass the Auto Scaling Group to it in the +constructor. Then add the Capacity Provider to the cluster. Finally, you can +refer to the Provider by its name in your service's or task's Capacity Provider +strategy. + +By default, an EC2 Capacity Provider will manage the Auto Scaling Group's size +for you. It will also enable managed termination protection, in order to prevent +EC2 Auto Scaling from terminating EC2 instances that have tasks running on +them. If you want to disable this behavior, set both `enableManagedScaling` to +and `enableManagedTerminationProtection` to `false`. + +```ts +const cluster = new ecs.Cluster(stack, 'Cluster', { + vpc, +}); + +const autoScalingGroup = new autoscaling.AutoScalingGroup(stack, 'ASG', { + vpc, + instanceType: new ec2.InstanceType('t2.micro'), + machineImage: ecs.EcsOptimizedImage.amazonLinux2(), + minCapacity: 0, + maxCapacity: 100, +}); + +const capacityProvider = new ecs.EC2CapacityProvider(stack, 'EC2CapacityProvider', { + autoScalingGroup, +}); +cluster.addEC2CapacityProvider(capacityProvider); -app.synth(); +const taskDefinition = new ecs.EC2TaskDefinition(stack, 'TaskDef'); + +taskDefinition.addContainer('web', { + image: ecs.ContainerImage.fromRegistry('amazon/amazon-ecs-sample'), memoryReservationMiB: 256, +}); + +new ecs.Ec2Service(stack, 'EC2Service', { + cluster, + taskDefinition, + capacityProviderStrategies: [ + { + capacityProvider: capacityProvider.capacityProviderName, + weight: 1, + } + ], +}); ``` ## Elastic Inference Accelerators diff --git a/packages/@aws-cdk/aws-ecs/lib/cluster.ts b/packages/@aws-cdk/aws-ecs/lib/cluster.ts index 625b0935aca29..e93424ef6ec94 100644 --- a/packages/@aws-cdk/aws-ecs/lib/cluster.ts +++ b/packages/@aws-cdk/aws-ecs/lib/cluster.ts @@ -52,23 +52,16 @@ export interface ClusterProps { * The capacity providers to add to the cluster * * @default - None. Currently only FARGATE and FARGATE_SPOT are supported. - * @deprecated Use {@link ClusterProps.enableFargateCapacityProvider} and {@link ClusterProps.enableFargateSpotCapacityProvider} instead. + * @deprecated Use {@link ClusterProps.enableFargateCapacityProviders} instead. */ readonly capacityProviders?: string[]; /** - * Whether to enable Fargate Capacity Provider + * Whether to enable Fargate Capacity Providers * * @default false */ - readonly enableFargateCapacityProvider?: boolean; - - /** - * Whether to enable Fargate Spot Capacity Provider - * - * @default false - */ - readonly enableFargateSpotCapacityProvider?: boolean; + readonly enableFargateCapacityProviders?: boolean; /** * If true CloudWatch Container Insights will be enabled for the cluster @@ -124,9 +117,14 @@ export class Cluster extends Resource implements ICluster { public readonly clusterName: string; /** - * The capacity providers associated with the cluster. + * The cluster-level (FARGATE, FARGATE_SPOT) capacity providers. + */ + private _clusterCapacityProviders: string[] = []; + + /** + * The EC2 Auto Scaling Group capacity providers associated with the cluster. */ - private _capacityProviders: ICapacityProvider[] = []; + private _ec2CapacityProviders: EC2CapacityProvider[] = []; /** * The AWS Cloud Map namespace to associate with the cluster. @@ -161,22 +159,15 @@ export class Cluster extends Resource implements ICluster { clusterSettings = [{ name: 'containerInsights', value: props.containerInsights ? ContainerInsights.ENABLED : ContainerInsights.DISABLED }]; } - if (props.enableFargateCapacityProvider || props.capacityProviders?.includes('FARGATE')) { - this.enableFargateCapacityProvider(); - } - if (props.enableFargateSpotCapacityProvider || props.capacityProviders?.includes('FARGATE_SPOT')) { - this.enableFargateSpotCapacityProvider(); + this._clusterCapacityProviders = props.capacityProviders ?? []; + if (props.enableFargateCapacityProviders) { + this.enableFargateCapacityProviders(); } const cluster = new CfnCluster(this, 'Resource', { clusterName: this.physicalName, clusterSettings, - capacityProviders: Lazy.list( - { - produce: () => this._capacityProviders.map(p => p.capacityProviderName).filter(p => p === 'FARGATE' || p === 'FARGATE_SPOT'), - }, - { omitEmpty: true }, - ), + capacityProviders: Lazy.list({ produce: () => this._clusterCapacityProviders }, { omitEmpty: true }), }); this.clusterArn = this.getResourceArnAttribute(cluster.attrArn, { @@ -202,24 +193,17 @@ export class Cluster extends Resource implements ICluster { // since it's harmless, but we'd prefer not to add unexpected new // resources to the stack which could surprise users working with // brown-field CDK apps and stacks. - Aspects.of(this).add(new MaybeCreateCapacityProviderAssociations(this, id, this._capacityProviders)); + Aspects.of(this).add(new MaybeCreateCapacityProviderAssociations(this, id, this._ec2CapacityProviders)); } /** - * Enable the Fargate capacity provider for this cluster. + * Enable the Fargate capacity providers for this cluster. */ - public enableFargateCapacityProvider() { - if (!this._capacityProviders.includes(FargateCapacityProvider)) { - this._capacityProviders.push(FargateCapacityProvider); - } - } - - /** - * Enable the Fargate Spot capacity provider for this cluster. - */ - public enableFargateSpotCapacityProvider() { - if (!this._capacityProviders.includes(FargateSpotCapacityProvider)) { - this._capacityProviders.push(FargateSpotCapacityProvider); + public enableFargateCapacityProviders() { + for (const provider of ['FARGATE', 'FARGATE_SPOT']) { + if (!this._clusterCapacityProviders.includes(provider)) { + this._clusterCapacityProviders.push(provider); + } } } @@ -293,35 +277,36 @@ export class Cluster extends Resource implements ICluster { * * @param provider the capacity provider to add to this cluster. */ - public addCapacityProvider(provider: ICapacityProvider | string, options: AddAutoScalingGroupCapacityOptions = {}) { - // Backward compatibility - if (typeof(provider) === 'string') { - switch (provider) { - case 'FARGATE': - this.addCapacityProvider(FargateCapacityProvider); - return; - case 'FARGATE_SPOT': - this.addCapacityProvider(FargateSpotCapacityProvider); - return; - default: - throw new Error('CapacityProvider not supported'); - } + public addCapacityProvider(provider: string) { + if (!(provider === 'FARGATE' || provider === 'FARGATE_SPOT')) { + throw new Error('CapacityProvider not supported'); } - // Don't add the same provider twice - if (this._capacityProviders.includes(provider)) { - return; + + if (!this._clusterCapacityProviders.includes(provider)) { + this._clusterCapacityProviders.push(provider); } + } - if (provider instanceof EC2CapacityProvider) { - this._hasEc2Capacity = true; - this.configureAutoScalingGroup(provider.autoScalingGroup, { - ...options, - // Don't enable the instance-draining lifecycle hook if managed termination protection is enabled - taskDrainTime: provider.enableManagedTerminationProtection ? Duration.seconds(0) : options.taskDrainTime, - }); + /** + * This method adds an Auto Scaling Group Capacity Provider to a cluster using + * the specified EC2 Capacity Provider. + * + * @param provider the capacity provider to add to this cluster. + */ + public addEC2CapacityProvider(provider: EC2CapacityProvider, options: AddAutoScalingGroupCapacityOptions = {}) { + // Don't add the same capacity provider more than once. + if (this._ec2CapacityProviders.includes(provider)) { + return; } - this._capacityProviders.push(provider); + this._hasEc2Capacity = true; + this.configureAutoScalingGroup(provider.autoScalingGroup, { + ...options, + // Don't enable the instance-draining lifecycle hook if managed termination protection is enabled + taskDrainTime: provider.enableManagedTerminationProtection ? Duration.seconds(0) : options.taskDrainTime, + }); + + this._ec2CapacityProviders.push(provider); } /** @@ -1074,38 +1059,6 @@ capacity provider. The weight value is taken into consideration after the base v readonly weight?: number; } -/** - * An ECS Capacity Provider - */ -export interface ICapacityProvider { - /** - * The name for the capacity provider. - * - * @default CloudFormation-generated name - */ - readonly capacityProviderName: string; -} - -/** - * A class used solely for generating the singleton objects that follow - */ -class NamedCapacityProvider implements ICapacityProvider { - public capacityProviderName: string; - constructor(name: string) { - this.capacityProviderName = name; - } -} - -/** - * Fargate capacity provider - */ -export const FargateCapacityProvider = new NamedCapacityProvider('FARGATE'); - -/** - * Fargate Spot capacity provider - */ -export const FargateSpotCapacityProvider = new NamedCapacityProvider('FARGATE_SPOT'); - /** * The options for creating an EC2 Capacity Provider. */ @@ -1119,10 +1072,8 @@ export interface EC2CapacityProviderProps extends AddAutoScalingGroupCapacityOpt /** * The autoscaling group to add as a Capacity Provider. - * [disable-awslint:ref-via-interface] is needed to enable managed termination - * protection. */ - readonly autoScalingGroup: autoscaling.AutoScalingGroup; + readonly autoScalingGroup: autoscaling.IAutoScalingGroup; /** * Whether to enable managed scaling @@ -1166,10 +1117,8 @@ export interface EC2CapacityProviderProps extends AddAutoScalingGroupCapacityOpt * ECS can manage the number of instances in the ASG to fit the tasks, and can * ensure that instances are not prematurely terminated while there are still tasks * running on them. - * [disable-awslint:ref-via-interface] is needed to enable managed termination - * protection. */ -export class EC2CapacityProvider extends CoreConstruct implements ICapacityProvider { +export class EC2CapacityProvider extends CoreConstruct { /** * Capacity provider name * @default Chosen by CloudFormation @@ -1189,8 +1138,10 @@ export class EC2CapacityProvider extends CoreConstruct implements ICapacityProvi constructor(scope: Construct, id: string, props: EC2CapacityProviderProps) { super(scope, id); - this.autoScalingGroup = props.autoScalingGroup; - this.enableManagedTerminationProtection = props.enableManagedTerminationProtection; + this.autoScalingGroup = props.autoScalingGroup as autoscaling.AutoScalingGroup; + + this.enableManagedTerminationProtection = + props.enableManagedTerminationProtection === undefined ? true : props.enableManagedTerminationProtection; if (this.enableManagedTerminationProtection) { this.autoScalingGroup.protectNewInstancesFromScaleIn(); @@ -1201,12 +1152,12 @@ export class EC2CapacityProvider extends CoreConstruct implements ICapacityProvi autoScalingGroupProvider: { autoScalingGroupArn: this.autoScalingGroup.autoScalingGroupName, managedScaling: { - status: (props.enableManagedScaling === undefined || props.enableManagedScaling) ? 'ENABLED' : 'DISABLED', + status: props.enableManagedScaling === false ? 'DISABLED' : 'ENABLED', targetCapacity: props.targetCapacityPercent || 100, maximumScalingStepSize: props.maximumScalingStepSize, minimumScalingStepSize: props.minimumScalingStepSize, }, - managedTerminationProtection: (props.enableManagedTerminationProtection === undefined || props.enableManagedTerminationProtection) ? 'ENABLED' : 'DISABLED', + managedTerminationProtection: this.enableManagedTerminationProtection ? 'ENABLED' : 'DISABLED', }, }); @@ -1221,9 +1172,9 @@ export class EC2CapacityProvider extends CoreConstruct implements ICapacityProvi class MaybeCreateCapacityProviderAssociations implements IAspect { private scope: CoreConstruct; private id: string; - private capacityProviders: ICapacityProvider[] + private capacityProviders: EC2CapacityProvider[] - constructor(scope: CoreConstruct, id: string, capacityProviders: ICapacityProvider[]) { + constructor(scope: CoreConstruct, id: string, capacityProviders: EC2CapacityProvider[]) { this.scope = scope; this.id = id; this.capacityProviders = capacityProviders; diff --git a/packages/@aws-cdk/aws-ecs/test/cluster.test.ts b/packages/@aws-cdk/aws-ecs/test/cluster.test.ts index 597edfecc3158..acfa6f41cdbfa 100644 --- a/packages/@aws-cdk/aws-ecs/test/cluster.test.ts +++ b/packages/@aws-cdk/aws-ecs/test/cluster.test.ts @@ -1719,12 +1719,12 @@ nodeunitShim({ // WHEN new ecs.Cluster(stack, 'EcsCluster', { - enableFargateSpotCapacityProvider: true, + enableFargateCapacityProviders: true, }); // THEN expect(stack).to(haveResource('AWS::ECS::Cluster', { - CapacityProviders: ['FARGATE_SPOT'], + CapacityProviders: ['FARGATE', 'FARGATE_SPOT'], })); test.done(); @@ -1737,8 +1737,7 @@ nodeunitShim({ // WHEN const cluster = new ecs.Cluster(stack, 'EcsCluster'); - cluster.enableFargateCapacityProvider(); - cluster.enableFargateSpotCapacityProvider(); + cluster.enableFargateCapacityProviders(); // THEN expect(stack).to(haveResource('AWS::ECS::Cluster', { @@ -1755,8 +1754,8 @@ nodeunitShim({ const cluster = new ecs.Cluster(stack, 'EcsCluster'); // WHEN - cluster.addCapacityProvider(ecs.FargateCapacityProvider); - cluster.addCapacityProvider(ecs.FargateCapacityProvider); // does not add twice + cluster.addCapacityProvider('FARGATE'); + cluster.addCapacityProvider('FARGATE'); // does not add twice // THEN expect(stack).to(haveResource('AWS::ECS::Cluster', { @@ -1830,7 +1829,40 @@ nodeunitShim({ test.done(); }, - 'capacity provider enables ASG new instance scale-in protection'(test: Test) { + 'can disable managed scaling for EC2 capacity provider'(test: Test) { + // GIVEN + const app = new cdk.App(); + const stack = new cdk.Stack(app, 'test'); + const vpc = new ec2.Vpc(stack, 'Vpc'); + const autoScalingGroup = new autoscaling.AutoScalingGroup(stack, 'asg', { + vpc, + instanceType: new ec2.InstanceType('bogus'), + machineImage: ecs.EcsOptimizedImage.amazonLinux2(), + }); + + // WHEN + new ecs.EC2CapacityProvider(stack, 'provider', { + autoScalingGroup, + enableManagedScaling: false, + }); + + // THEN + expect(stack).to(haveResource('AWS::ECS::CapacityProvider', { + AutoScalingGroupProvider: { + AutoScalingGroupArn: { + Ref: 'asgASG4D014670', + }, + ManagedScaling: { + Status: 'DISABLED', + TargetCapacity: 100, + }, + ManagedTerminationProtection: 'ENABLED', + }, + })); + test.done(); + }, + + 'capacity provider enables ASG new instance scale-in protection by default'(test: Test) { // GIVEN const app = new cdk.App(); const stack = new cdk.Stack(app, 'test'); @@ -1844,7 +1876,6 @@ nodeunitShim({ // WHEN new ecs.EC2CapacityProvider(stack, 'provider', { autoScalingGroup, - enableManagedTerminationProtection: true, }); // THEN @@ -1898,12 +1929,11 @@ nodeunitShim({ }); // These should not be added at the association level - cluster.enableFargateCapacityProvider(); - cluster.enableFargateSpotCapacityProvider(); + cluster.enableFargateCapacityProviders(); // Ensure not added twice - cluster.addCapacityProvider(capacityProvider); - cluster.addCapacityProvider(capacityProvider); + cluster.addEC2CapacityProvider(capacityProvider); + cluster.addEC2CapacityProvider(capacityProvider); // THEN expect(stack).to(haveResource('AWS::ECS::ClusterCapacityProviderAssociations', { diff --git a/packages/@aws-cdk/aws-ecs/test/ec2/ec2-service.test.ts b/packages/@aws-cdk/aws-ecs/test/ec2/ec2-service.test.ts index 3798a2bd502f0..ee0b2b9cf0de4 100644 --- a/packages/@aws-cdk/aws-ecs/test/ec2/ec2-service.test.ts +++ b/packages/@aws-cdk/aws-ecs/test/ec2/ec2-service.test.ts @@ -254,7 +254,7 @@ nodeunitShim({ autoScalingGroup, enableManagedTerminationProtection: false, }); - cluster.addCapacityProvider(capacityProvider); + cluster.addEC2CapacityProvider(capacityProvider); const taskDefinition = new ecs.TaskDefinition(stack, 'ServerTask', { compatibility: ecs.Compatibility.EC2, diff --git a/packages/@aws-cdk/aws-ecs/test/ec2/integ.capacity-provider.expected.json b/packages/@aws-cdk/aws-ecs/test/ec2/integ.capacity-provider.expected.json new file mode 100644 index 0000000000000..d4e008750fe93 --- /dev/null +++ b/packages/@aws-cdk/aws-ecs/test/ec2/integ.capacity-provider.expected.json @@ -0,0 +1,655 @@ +{ + "Resources": { + "Vpc8378EB38": { + "Type": "AWS::EC2::VPC", + "Properties": { + "CidrBlock": "10.0.0.0/16", + "EnableDnsHostnames": true, + "EnableDnsSupport": true, + "InstanceTenancy": "default", + "Tags": [ + { + "Key": "Name", + "Value": "integ-ec2-capacity-provider/Vpc" + } + ] + } + }, + "VpcPublicSubnet1Subnet5C2D37C4": { + "Type": "AWS::EC2::Subnet", + "Properties": { + "CidrBlock": "10.0.0.0/18", + "VpcId": { + "Ref": "Vpc8378EB38" + }, + "AvailabilityZone": "test-region-1a", + "MapPublicIpOnLaunch": true, + "Tags": [ + { + "Key": "aws-cdk:subnet-name", + "Value": "Public" + }, + { + "Key": "aws-cdk:subnet-type", + "Value": "Public" + }, + { + "Key": "Name", + "Value": "integ-ec2-capacity-provider/Vpc/PublicSubnet1" + } + ] + } + }, + "VpcPublicSubnet1RouteTable6C95E38E": { + "Type": "AWS::EC2::RouteTable", + "Properties": { + "VpcId": { + "Ref": "Vpc8378EB38" + }, + "Tags": [ + { + "Key": "Name", + "Value": "integ-ec2-capacity-provider/Vpc/PublicSubnet1" + } + ] + } + }, + "VpcPublicSubnet1RouteTableAssociation97140677": { + "Type": "AWS::EC2::SubnetRouteTableAssociation", + "Properties": { + "RouteTableId": { + "Ref": "VpcPublicSubnet1RouteTable6C95E38E" + }, + "SubnetId": { + "Ref": "VpcPublicSubnet1Subnet5C2D37C4" + } + } + }, + "VpcPublicSubnet1DefaultRoute3DA9E72A": { + "Type": "AWS::EC2::Route", + "Properties": { + "RouteTableId": { + "Ref": "VpcPublicSubnet1RouteTable6C95E38E" + }, + "DestinationCidrBlock": "0.0.0.0/0", + "GatewayId": { + "Ref": "VpcIGWD7BA715C" + } + }, + "DependsOn": [ + "VpcVPCGWBF912B6E" + ] + }, + "VpcPublicSubnet1EIPD7E02669": { + "Type": "AWS::EC2::EIP", + "Properties": { + "Domain": "vpc", + "Tags": [ + { + "Key": "Name", + "Value": "integ-ec2-capacity-provider/Vpc/PublicSubnet1" + } + ] + } + }, + "VpcPublicSubnet1NATGateway4D7517AA": { + "Type": "AWS::EC2::NatGateway", + "Properties": { + "AllocationId": { + "Fn::GetAtt": [ + "VpcPublicSubnet1EIPD7E02669", + "AllocationId" + ] + }, + "SubnetId": { + "Ref": "VpcPublicSubnet1Subnet5C2D37C4" + }, + "Tags": [ + { + "Key": "Name", + "Value": "integ-ec2-capacity-provider/Vpc/PublicSubnet1" + } + ] + } + }, + "VpcPublicSubnet2Subnet691E08A3": { + "Type": "AWS::EC2::Subnet", + "Properties": { + "CidrBlock": "10.0.64.0/18", + "VpcId": { + "Ref": "Vpc8378EB38" + }, + "AvailabilityZone": "test-region-1b", + "MapPublicIpOnLaunch": true, + "Tags": [ + { + "Key": "aws-cdk:subnet-name", + "Value": "Public" + }, + { + "Key": "aws-cdk:subnet-type", + "Value": "Public" + }, + { + "Key": "Name", + "Value": "integ-ec2-capacity-provider/Vpc/PublicSubnet2" + } + ] + } + }, + "VpcPublicSubnet2RouteTable94F7E489": { + "Type": "AWS::EC2::RouteTable", + "Properties": { + "VpcId": { + "Ref": "Vpc8378EB38" + }, + "Tags": [ + { + "Key": "Name", + "Value": "integ-ec2-capacity-provider/Vpc/PublicSubnet2" + } + ] + } + }, + "VpcPublicSubnet2RouteTableAssociationDD5762D8": { + "Type": "AWS::EC2::SubnetRouteTableAssociation", + "Properties": { + "RouteTableId": { + "Ref": "VpcPublicSubnet2RouteTable94F7E489" + }, + "SubnetId": { + "Ref": "VpcPublicSubnet2Subnet691E08A3" + } + } + }, + "VpcPublicSubnet2DefaultRoute97F91067": { + "Type": "AWS::EC2::Route", + "Properties": { + "RouteTableId": { + "Ref": "VpcPublicSubnet2RouteTable94F7E489" + }, + "DestinationCidrBlock": "0.0.0.0/0", + "GatewayId": { + "Ref": "VpcIGWD7BA715C" + } + }, + "DependsOn": [ + "VpcVPCGWBF912B6E" + ] + }, + "VpcPublicSubnet2EIP3C605A87": { + "Type": "AWS::EC2::EIP", + "Properties": { + "Domain": "vpc", + "Tags": [ + { + "Key": "Name", + "Value": "integ-ec2-capacity-provider/Vpc/PublicSubnet2" + } + ] + } + }, + "VpcPublicSubnet2NATGateway9182C01D": { + "Type": "AWS::EC2::NatGateway", + "Properties": { + "AllocationId": { + "Fn::GetAtt": [ + "VpcPublicSubnet2EIP3C605A87", + "AllocationId" + ] + }, + "SubnetId": { + "Ref": "VpcPublicSubnet2Subnet691E08A3" + }, + "Tags": [ + { + "Key": "Name", + "Value": "integ-ec2-capacity-provider/Vpc/PublicSubnet2" + } + ] + } + }, + "VpcPrivateSubnet1Subnet536B997A": { + "Type": "AWS::EC2::Subnet", + "Properties": { + "CidrBlock": "10.0.128.0/18", + "VpcId": { + "Ref": "Vpc8378EB38" + }, + "AvailabilityZone": "test-region-1a", + "MapPublicIpOnLaunch": false, + "Tags": [ + { + "Key": "aws-cdk:subnet-name", + "Value": "Private" + }, + { + "Key": "aws-cdk:subnet-type", + "Value": "Private" + }, + { + "Key": "Name", + "Value": "integ-ec2-capacity-provider/Vpc/PrivateSubnet1" + } + ] + } + }, + "VpcPrivateSubnet1RouteTableB2C5B500": { + "Type": "AWS::EC2::RouteTable", + "Properties": { + "VpcId": { + "Ref": "Vpc8378EB38" + }, + "Tags": [ + { + "Key": "Name", + "Value": "integ-ec2-capacity-provider/Vpc/PrivateSubnet1" + } + ] + } + }, + "VpcPrivateSubnet1RouteTableAssociation70C59FA6": { + "Type": "AWS::EC2::SubnetRouteTableAssociation", + "Properties": { + "RouteTableId": { + "Ref": "VpcPrivateSubnet1RouteTableB2C5B500" + }, + "SubnetId": { + "Ref": "VpcPrivateSubnet1Subnet536B997A" + } + } + }, + "VpcPrivateSubnet1DefaultRouteBE02A9ED": { + "Type": "AWS::EC2::Route", + "Properties": { + "RouteTableId": { + "Ref": "VpcPrivateSubnet1RouteTableB2C5B500" + }, + "DestinationCidrBlock": "0.0.0.0/0", + "NatGatewayId": { + "Ref": "VpcPublicSubnet1NATGateway4D7517AA" + } + } + }, + "VpcPrivateSubnet2Subnet3788AAA1": { + "Type": "AWS::EC2::Subnet", + "Properties": { + "CidrBlock": "10.0.192.0/18", + "VpcId": { + "Ref": "Vpc8378EB38" + }, + "AvailabilityZone": "test-region-1b", + "MapPublicIpOnLaunch": false, + "Tags": [ + { + "Key": "aws-cdk:subnet-name", + "Value": "Private" + }, + { + "Key": "aws-cdk:subnet-type", + "Value": "Private" + }, + { + "Key": "Name", + "Value": "integ-ec2-capacity-provider/Vpc/PrivateSubnet2" + } + ] + } + }, + "VpcPrivateSubnet2RouteTableA678073B": { + "Type": "AWS::EC2::RouteTable", + "Properties": { + "VpcId": { + "Ref": "Vpc8378EB38" + }, + "Tags": [ + { + "Key": "Name", + "Value": "integ-ec2-capacity-provider/Vpc/PrivateSubnet2" + } + ] + } + }, + "VpcPrivateSubnet2RouteTableAssociationA89CAD56": { + "Type": "AWS::EC2::SubnetRouteTableAssociation", + "Properties": { + "RouteTableId": { + "Ref": "VpcPrivateSubnet2RouteTableA678073B" + }, + "SubnetId": { + "Ref": "VpcPrivateSubnet2Subnet3788AAA1" + } + } + }, + "VpcPrivateSubnet2DefaultRoute060D2087": { + "Type": "AWS::EC2::Route", + "Properties": { + "RouteTableId": { + "Ref": "VpcPrivateSubnet2RouteTableA678073B" + }, + "DestinationCidrBlock": "0.0.0.0/0", + "NatGatewayId": { + "Ref": "VpcPublicSubnet2NATGateway9182C01D" + } + } + }, + "VpcIGWD7BA715C": { + "Type": "AWS::EC2::InternetGateway", + "Properties": { + "Tags": [ + { + "Key": "Name", + "Value": "integ-ec2-capacity-provider/Vpc" + } + ] + } + }, + "VpcVPCGWBF912B6E": { + "Type": "AWS::EC2::VPCGatewayAttachment", + "Properties": { + "VpcId": { + "Ref": "Vpc8378EB38" + }, + "InternetGatewayId": { + "Ref": "VpcIGWD7BA715C" + } + } + }, + "EC2CPClusterD5F0FD32": { + "Type": "AWS::ECS::Cluster" + }, + "EC2CPCluster4CFED4DD": { + "Type": "AWS::ECS::ClusterCapacityProviderAssociations", + "Properties": { + "CapacityProviders": [ + { + "Ref": "EC2CapacityProvider5A2E35CD" + } + ], + "Cluster": { + "Ref": "EC2CPClusterD5F0FD32" + }, + "DefaultCapacityProviderStrategy": [] + } + }, + "TaskDefTaskRole1EDB4A67": { + "Type": "AWS::IAM::Role", + "Properties": { + "AssumeRolePolicyDocument": { + "Statement": [ + { + "Action": "sts:AssumeRole", + "Effect": "Allow", + "Principal": { + "Service": "ecs-tasks.amazonaws.com" + } + } + ], + "Version": "2012-10-17" + } + } + }, + "TaskDef54694570": { + "Type": "AWS::ECS::TaskDefinition", + "Properties": { + "ContainerDefinitions": [ + { + "Essential": true, + "Image": "amazon/amazon-ecs-sample", + "MemoryReservation": 256, + "Name": "web" + } + ], + "Family": "integec2capacityproviderTaskDefA6140A6B", + "NetworkMode": "bridge", + "RequiresCompatibilities": [ + "EC2" + ], + "TaskRoleArn": { + "Fn::GetAtt": [ + "TaskDefTaskRole1EDB4A67", + "Arn" + ] + } + } + }, + "ASGInstanceSecurityGroup0525485D": { + "Type": "AWS::EC2::SecurityGroup", + "Properties": { + "GroupDescription": "integ-ec2-capacity-provider/ASG/InstanceSecurityGroup", + "SecurityGroupEgress": [ + { + "CidrIp": "0.0.0.0/0", + "Description": "Allow all outbound traffic by default", + "IpProtocol": "-1" + } + ], + "Tags": [ + { + "Key": "Name", + "Value": "integ-ec2-capacity-provider/ASG" + } + ], + "VpcId": { + "Ref": "Vpc8378EB38" + } + } + }, + "ASGInstanceRoleE263A41B": { + "Type": "AWS::IAM::Role", + "Properties": { + "AssumeRolePolicyDocument": { + "Statement": [ + { + "Action": "sts:AssumeRole", + "Effect": "Allow", + "Principal": { + "Service": { + "Fn::Join": [ + "", + [ + "ec2.", + { + "Ref": "AWS::URLSuffix" + } + ] + ] + } + } + } + ], + "Version": "2012-10-17" + }, + "Tags": [ + { + "Key": "Name", + "Value": "integ-ec2-capacity-provider/ASG" + } + ] + } + }, + "ASGInstanceRoleDefaultPolicy7636D8BF": { + "Type": "AWS::IAM::Policy", + "Properties": { + "PolicyDocument": { + "Statement": [ + { + "Action": [ + "ecs:DeregisterContainerInstance", + "ecs:RegisterContainerInstance", + "ecs:Submit*" + ], + "Effect": "Allow", + "Resource": { + "Fn::GetAtt": [ + "EC2CPClusterD5F0FD32", + "Arn" + ] + } + }, + { + "Action": [ + "ecs:Poll", + "ecs:StartTelemetrySession" + ], + "Condition": { + "ArnEquals": { + "ecs:cluster": { + "Fn::GetAtt": [ + "EC2CPClusterD5F0FD32", + "Arn" + ] + } + } + }, + "Effect": "Allow", + "Resource": "*" + }, + { + "Action": [ + "ecs:DiscoverPollEndpoint", + "ecr:GetAuthorizationToken", + "logs:CreateLogStream", + "logs:PutLogEvents" + ], + "Effect": "Allow", + "Resource": "*" + } + ], + "Version": "2012-10-17" + }, + "PolicyName": "ASGInstanceRoleDefaultPolicy7636D8BF", + "Roles": [ + { + "Ref": "ASGInstanceRoleE263A41B" + } + ] + } + }, + "ASGInstanceProfile0A2834D7": { + "Type": "AWS::IAM::InstanceProfile", + "Properties": { + "Roles": [ + { + "Ref": "ASGInstanceRoleE263A41B" + } + ] + } + }, + "ASGLaunchConfigC00AF12B": { + "Type": "AWS::AutoScaling::LaunchConfiguration", + "Properties": { + "ImageId": { + "Ref": "SsmParameterValueawsserviceecsoptimizedamiamazonlinux2recommendedimageidC96584B6F00A464EAD1953AFF4B05118Parameter" + }, + "InstanceType": "t2.micro", + "IamInstanceProfile": { + "Ref": "ASGInstanceProfile0A2834D7" + }, + "SecurityGroups": [ + { + "Fn::GetAtt": [ + "ASGInstanceSecurityGroup0525485D", + "GroupId" + ] + } + ], + "UserData": { + "Fn::Base64": { + "Fn::Join": [ + "", + [ + "#!/bin/bash\necho ECS_CLUSTER=", + { + "Ref": "EC2CPClusterD5F0FD32" + }, + " >> /etc/ecs/ecs.config\nsudo iptables --insert FORWARD 1 --in-interface docker+ --destination 169.254.169.254/32 --jump DROP\nsudo service iptables save\necho ECS_AWSVPC_BLOCK_IMDS=true >> /etc/ecs/ecs.config" + ] + ] + } + } + }, + "DependsOn": [ + "ASGInstanceRoleDefaultPolicy7636D8BF", + "ASGInstanceRoleE263A41B" + ] + }, + "ASG46ED3070": { + "Type": "AWS::AutoScaling::AutoScalingGroup", + "Properties": { + "MaxSize": "1", + "MinSize": "1", + "LaunchConfigurationName": { + "Ref": "ASGLaunchConfigC00AF12B" + }, + "NewInstancesProtectedFromScaleIn": true, + "Tags": [ + { + "Key": "Name", + "PropagateAtLaunch": true, + "Value": "integ-ec2-capacity-provider/ASG" + } + ], + "VPCZoneIdentifier": [ + { + "Ref": "VpcPrivateSubnet1Subnet536B997A" + }, + { + "Ref": "VpcPrivateSubnet2Subnet3788AAA1" + } + ] + }, + "UpdatePolicy": { + "AutoScalingScheduledAction": { + "IgnoreUnmodifiedGroupSizeProperties": true + } + } + }, + "EC2CapacityProvider5A2E35CD": { + "Type": "AWS::ECS::CapacityProvider", + "Properties": { + "AutoScalingGroupProvider": { + "AutoScalingGroupArn": { + "Ref": "ASG46ED3070" + }, + "ManagedScaling": { + "Status": "ENABLED", + "TargetCapacity": 100 + }, + "ManagedTerminationProtection": "ENABLED" + } + } + }, + "EC2Service5392EF94": { + "Type": "AWS::ECS::Service", + "Properties": { + "CapacityProviderStrategy": [ + { + "CapacityProvider": { + "Ref": "EC2CapacityProvider5A2E35CD" + }, + "Weight": 1 + } + ], + "Cluster": { + "Ref": "EC2CPClusterD5F0FD32" + }, + "DeploymentConfiguration": { + "MaximumPercent": 200, + "MinimumHealthyPercent": 50 + }, + "EnableECSManagedTags": false, + "SchedulingStrategy": "REPLICA", + "TaskDefinition": { + "Ref": "TaskDef54694570" + } + } + } + }, + "Parameters": { + "SsmParameterValueawsserviceecsoptimizedamiamazonlinux2recommendedimageidC96584B6F00A464EAD1953AFF4B05118Parameter": { + "Type": "AWS::SSM::Parameter::Value", + "Default": "/aws/service/ecs/optimized-ami/amazon-linux-2/recommended/image_id" + } + } +} \ No newline at end of file diff --git a/packages/@aws-cdk/aws-ecs/test/ec2/integ.capacity-provider.ts b/packages/@aws-cdk/aws-ecs/test/ec2/integ.capacity-provider.ts new file mode 100644 index 0000000000000..06b5f29e88437 --- /dev/null +++ b/packages/@aws-cdk/aws-ecs/test/ec2/integ.capacity-provider.ts @@ -0,0 +1,46 @@ +import * as autoscaling from '@aws-cdk/aws-autoscaling'; +import * as ec2 from '@aws-cdk/aws-ec2'; +import * as cdk from '@aws-cdk/core'; +import * as ecs from '../../lib'; + +const app = new cdk.App(); +const stack = new cdk.Stack(app, 'integ-ec2-capacity-provider'); + +const vpc = new ec2.Vpc(stack, 'Vpc', { maxAzs: 2 }); + +const cluster = new ecs.Cluster(stack, 'EC2CPCluster', { + vpc, +}); + +const taskDefinition = new ecs.Ec2TaskDefinition(stack, 'TaskDef'); + +taskDefinition.addContainer('web', { + image: ecs.ContainerImage.fromRegistry('amazon/amazon-ecs-sample'), + memoryReservationMiB: 256, +}); + +const autoScalingGroup = new autoscaling.AutoScalingGroup(stack, 'ASG', { + vpc, + instanceType: new ec2.InstanceType('t2.micro'), + machineImage: ecs.EcsOptimizedImage.amazonLinux2(), +}); + +const cp = new ecs.EC2CapacityProvider(stack, 'EC2CapacityProvider', { + autoScalingGroup, +}); + +cluster.addEC2CapacityProvider(cp); + +new ecs.Ec2Service(stack, 'EC2Service', { + cluster, + taskDefinition, + capacityProviderStrategies: [ + { + capacityProvider: cp.capacityProviderName, + weight: 1, + }, + ], +}); + +app.synth(); + diff --git a/packages/@aws-cdk/aws-ecs/test/fargate/fargate-service.test.ts b/packages/@aws-cdk/aws-ecs/test/fargate/fargate-service.test.ts index f720ca8d5ae00..426f81a5e8230 100644 --- a/packages/@aws-cdk/aws-ecs/test/fargate/fargate-service.test.ts +++ b/packages/@aws-cdk/aws-ecs/test/fargate/fargate-service.test.ts @@ -202,8 +202,7 @@ nodeunitShim({ const cluster = new ecs.Cluster(stack, 'EcsCluster', { vpc, }); - cluster.enableFargateCapacityProvider(); - cluster.enableFargateSpotCapacityProvider(); + cluster.enableFargateCapacityProviders(); const taskDefinition = new ecs.FargateTaskDefinition(stack, 'FargateTaskDef'); From 1b9a8604a45cf2718eec21545fc78f472daf9688 Mon Sep 17 00:00:00 2001 From: "Michael S. Fischer" Date: Tue, 11 May 2021 14:10:38 -0700 Subject: [PATCH 4/9] Update packages/@aws-cdk/aws-ecs/README.md Co-authored-by: Hsing-Hui Hsu --- packages/@aws-cdk/aws-ecs/README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/@aws-cdk/aws-ecs/README.md b/packages/@aws-cdk/aws-ecs/README.md index f94050e300602..27cd13cb746f0 100644 --- a/packages/@aws-cdk/aws-ecs/README.md +++ b/packages/@aws-cdk/aws-ecs/README.md @@ -725,8 +725,8 @@ ecsService.associateCloudMapService({ ## Capacity Providers -There are two major families of Capacity Providers: Fargate and EC2 (via Auto -Scaling Groups. Both are supported. +There are two major families of Capacity Providers: [AWS Fargate](https://docs.aws.amazon.com/AmazonECS/latest/developerguide/fargate-capacity-providers.html) (including Fargate Spot) and EC2 [Auto +Scaling Group](https://docs.aws.amazon.com/AmazonECS/latest/developerguide/asg-capacity-providers.html) Capacity Providers. Both are supported. ### Fargate Capacity Providers From a75f3c6861f97996d6432b5114042fb1530335b4 Mon Sep 17 00:00:00 2001 From: "Michael S. Fischer" Date: Tue, 11 May 2021 14:10:47 -0700 Subject: [PATCH 5/9] Update packages/@aws-cdk/aws-ecs/README.md Co-authored-by: Hsing-Hui Hsu --- packages/@aws-cdk/aws-ecs/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/@aws-cdk/aws-ecs/README.md b/packages/@aws-cdk/aws-ecs/README.md index 27cd13cb746f0..375d0ef5b6f95 100644 --- a/packages/@aws-cdk/aws-ecs/README.md +++ b/packages/@aws-cdk/aws-ecs/README.md @@ -733,7 +733,7 @@ Scaling Group](https://docs.aws.amazon.com/AmazonECS/latest/developerguide/asg-c To enable Fargate capacity providers, you can either set `enableFargateCapacityProviders` to `true` when creating your cluster, or by invoking the `enableFargateCapacityProviders()` method after creating your -cluster. +cluster. This will add both `FARGATE` and `FARGATE_SPOT` as available capacity providers on your cluster. ```ts const cluster = new ecs.Cluster(stack, 'FargateCPCluster', { From 287a6d9cc4852cafa72610448624f5877dd254d8 Mon Sep 17 00:00:00 2001 From: "Michael S. Fischer" Date: Tue, 11 May 2021 14:10:55 -0700 Subject: [PATCH 6/9] Update packages/@aws-cdk/aws-ecs/README.md Co-authored-by: Hsing-Hui Hsu --- packages/@aws-cdk/aws-ecs/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/@aws-cdk/aws-ecs/README.md b/packages/@aws-cdk/aws-ecs/README.md index 375d0ef5b6f95..277d082f32311 100644 --- a/packages/@aws-cdk/aws-ecs/README.md +++ b/packages/@aws-cdk/aws-ecs/README.md @@ -763,7 +763,7 @@ new ecs.FargateService(stack, 'FargateService', { }); ``` -### EC2 Capacity Providers +### Auto Scaling Group Capacity Providers To add an EC2 Capacity Provider, first create an EC2 Auto Scaling Group. Then, create an `EC2CapacityProvider` and pass the Auto Scaling Group to it in the From ccb9dc118c7bea88a2296fc9e764df2e17f10143 Mon Sep 17 00:00:00 2001 From: "Michael S. Fischer" Date: Tue, 11 May 2021 14:11:05 -0700 Subject: [PATCH 7/9] Update packages/@aws-cdk/aws-ecs/README.md Co-authored-by: Hsing-Hui Hsu --- packages/@aws-cdk/aws-ecs/README.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/packages/@aws-cdk/aws-ecs/README.md b/packages/@aws-cdk/aws-ecs/README.md index 277d082f32311..d5ac1ed45cdf9 100644 --- a/packages/@aws-cdk/aws-ecs/README.md +++ b/packages/@aws-cdk/aws-ecs/README.md @@ -765,13 +765,13 @@ new ecs.FargateService(stack, 'FargateService', { ### Auto Scaling Group Capacity Providers -To add an EC2 Capacity Provider, first create an EC2 Auto Scaling Group. Then, -create an `EC2CapacityProvider` and pass the Auto Scaling Group to it in the +To add an Auto Scaling Group Capacity Provider, first create an EC2 Auto Scaling Group. Then, +create an `AsgCapacityProvider` and pass the Auto Scaling Group to it in the constructor. Then add the Capacity Provider to the cluster. Finally, you can refer to the Provider by its name in your service's or task's Capacity Provider strategy. -By default, an EC2 Capacity Provider will manage the Auto Scaling Group's size +By default, an Auto Scaling Group Capacity Provider will manage the Auto Scaling Group's size for you. It will also enable managed termination protection, in order to prevent EC2 Auto Scaling from terminating EC2 instances that have tasks running on them. If you want to disable this behavior, set both `enableManagedScaling` to From 3627bd968a49e870de19260b627f616f985f7cb9 Mon Sep 17 00:00:00 2001 From: "Michael S. Fischer" Date: Tue, 11 May 2021 14:11:18 -0700 Subject: [PATCH 8/9] Update packages/@aws-cdk/aws-ecs/test/cluster.test.ts Co-authored-by: Hsing-Hui Hsu --- packages/@aws-cdk/aws-ecs/test/cluster.test.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/@aws-cdk/aws-ecs/test/cluster.test.ts b/packages/@aws-cdk/aws-ecs/test/cluster.test.ts index acfa6f41cdbfa..0269349948554 100644 --- a/packages/@aws-cdk/aws-ecs/test/cluster.test.ts +++ b/packages/@aws-cdk/aws-ecs/test/cluster.test.ts @@ -1797,7 +1797,7 @@ nodeunitShim({ test.done(); }, - 'creates capacity providers with expected defaults'(test: Test) { + 'creates ASG capacity providers with expected defaults'(test: Test) { // GIVEN const app = new cdk.App(); const stack = new cdk.Stack(app, 'test'); From ecc996620173660a36e7cde1fabca3bff6e61ace Mon Sep 17 00:00:00 2001 From: Michael Fischer Date: Tue, 11 May 2021 14:42:00 -0700 Subject: [PATCH 9/9] Updated to reflect review feedback --- packages/@aws-cdk/aws-ecs/README.md | 41 +++++---- packages/@aws-cdk/aws-ecs/lib/cluster.ts | 87 ++++++++++--------- .../@aws-cdk/aws-ecs/test/cluster.test.ts | 24 +++-- .../aws-ecs/test/ec2/ec2-service.test.ts | 4 +- .../test/ec2/integ.capacity-provider.ts | 4 +- .../test/fargate/fargate-service.test.ts | 3 +- 6 files changed, 84 insertions(+), 79 deletions(-) diff --git a/packages/@aws-cdk/aws-ecs/README.md b/packages/@aws-cdk/aws-ecs/README.md index d5ac1ed45cdf9..5e63747de137f 100644 --- a/packages/@aws-cdk/aws-ecs/README.md +++ b/packages/@aws-cdk/aws-ecs/README.md @@ -725,15 +725,19 @@ ecsService.associateCloudMapService({ ## Capacity Providers -There are two major families of Capacity Providers: [AWS Fargate](https://docs.aws.amazon.com/AmazonECS/latest/developerguide/fargate-capacity-providers.html) (including Fargate Spot) and EC2 [Auto -Scaling Group](https://docs.aws.amazon.com/AmazonECS/latest/developerguide/asg-capacity-providers.html) Capacity Providers. Both are supported. +There are two major families of Capacity Providers: [AWS +Fargate](https://docs.aws.amazon.com/AmazonECS/latest/developerguide/fargate-capacity-providers.html) +(including Fargate Spot) and EC2 [Auto Scaling +Group](https://docs.aws.amazon.com/AmazonECS/latest/developerguide/asg-capacity-providers.html) +Capacity Providers. Both are supported. ### Fargate Capacity Providers To enable Fargate capacity providers, you can either set `enableFargateCapacityProviders` to `true` when creating your cluster, or by invoking the `enableFargateCapacityProviders()` method after creating your -cluster. This will add both `FARGATE` and `FARGATE_SPOT` as available capacity providers on your cluster. +cluster. This will add both `FARGATE` and `FARGATE_SPOT` as available capacity +providers on your cluster. ```ts const cluster = new ecs.Cluster(stack, 'FargateCPCluster', { @@ -765,17 +769,17 @@ new ecs.FargateService(stack, 'FargateService', { ### Auto Scaling Group Capacity Providers -To add an Auto Scaling Group Capacity Provider, first create an EC2 Auto Scaling Group. Then, -create an `AsgCapacityProvider` and pass the Auto Scaling Group to it in the -constructor. Then add the Capacity Provider to the cluster. Finally, you can -refer to the Provider by its name in your service's or task's Capacity Provider -strategy. +To add an Auto Scaling Group Capacity Provider, first create an EC2 Auto Scaling +Group. Then, create an `AsgCapacityProvider` and pass the Auto Scaling Group to +it in the constructor. Then add the Capacity Provider to the cluster. Finally, +you can refer to the Provider by its name in your service's or task's Capacity +Provider strategy. -By default, an Auto Scaling Group Capacity Provider will manage the Auto Scaling Group's size -for you. It will also enable managed termination protection, in order to prevent -EC2 Auto Scaling from terminating EC2 instances that have tasks running on -them. If you want to disable this behavior, set both `enableManagedScaling` to -and `enableManagedTerminationProtection` to `false`. +By default, an Auto Scaling Group Capacity Provider will manage the Auto Scaling +Group's size for you. It will also enable managed termination protection, in +order to prevent EC2 Auto Scaling from terminating EC2 instances that have tasks +running on them. If you want to disable this behavior, set both +`enableManagedScaling` to and `enableManagedTerminationProtection` to `false`. ```ts const cluster = new ecs.Cluster(stack, 'Cluster', { @@ -790,15 +794,16 @@ const autoScalingGroup = new autoscaling.AutoScalingGroup(stack, 'ASG', { maxCapacity: 100, }); -const capacityProvider = new ecs.EC2CapacityProvider(stack, 'EC2CapacityProvider', { +const capacityProvider = new ecs.AsgCapacityProvider(stack, 'AsgCapacityProvider', { autoScalingGroup, }); -cluster.addEC2CapacityProvider(capacityProvider); +cluster.addAsgCapacityProvider(capacityProvider); -const taskDefinition = new ecs.EC2TaskDefinition(stack, 'TaskDef'); +const taskDefinition = new ecs.Ec2TaskDefinition(stack, 'TaskDef'); taskDefinition.addContainer('web', { - image: ecs.ContainerImage.fromRegistry('amazon/amazon-ecs-sample'), memoryReservationMiB: 256, + image: ecs.ContainerImage.fromRegistry('amazon/amazon-ecs-sample', + memoryReservationMiB: 256, }); new ecs.Ec2Service(stack, 'EC2Service', { @@ -818,7 +823,7 @@ new ecs.Ec2Service(stack, 'EC2Service', { Currently, this feature is only supported for services with EC2 launch types. To add elastic inference accelerators to your EC2 instance, first add -`inferenceAccelerators` field to the EC2TaskDefinition and set the `deviceName` +`inferenceAccelerators` field to the Ec2TaskDefinition and set the `deviceName` and `deviceType` properties. ```ts diff --git a/packages/@aws-cdk/aws-ecs/lib/cluster.ts b/packages/@aws-cdk/aws-ecs/lib/cluster.ts index e93424ef6ec94..294a88fcb1858 100644 --- a/packages/@aws-cdk/aws-ecs/lib/cluster.ts +++ b/packages/@aws-cdk/aws-ecs/lib/cluster.ts @@ -119,12 +119,12 @@ export class Cluster extends Resource implements ICluster { /** * The cluster-level (FARGATE, FARGATE_SPOT) capacity providers. */ - private _clusterCapacityProviders: string[] = []; + private _fargateCapacityProviders: string[] = []; /** * The EC2 Auto Scaling Group capacity providers associated with the cluster. */ - private _ec2CapacityProviders: EC2CapacityProvider[] = []; + private _asgCapacityProviders: AsgCapacityProvider[] = []; /** * The AWS Cloud Map namespace to associate with the cluster. @@ -159,7 +159,7 @@ export class Cluster extends Resource implements ICluster { clusterSettings = [{ name: 'containerInsights', value: props.containerInsights ? ContainerInsights.ENABLED : ContainerInsights.DISABLED }]; } - this._clusterCapacityProviders = props.capacityProviders ?? []; + this._fargateCapacityProviders = props.capacityProviders ?? []; if (props.enableFargateCapacityProviders) { this.enableFargateCapacityProviders(); } @@ -167,7 +167,7 @@ export class Cluster extends Resource implements ICluster { const cluster = new CfnCluster(this, 'Resource', { clusterName: this.physicalName, clusterSettings, - capacityProviders: Lazy.list({ produce: () => this._clusterCapacityProviders }, { omitEmpty: true }), + capacityProviders: Lazy.list({ produce: () => this._fargateCapacityProviders }, { omitEmpty: true }), }); this.clusterArn = this.getResourceArnAttribute(cluster.attrArn, { @@ -193,7 +193,7 @@ export class Cluster extends Resource implements ICluster { // since it's harmless, but we'd prefer not to add unexpected new // resources to the stack which could surprise users working with // brown-field CDK apps and stacks. - Aspects.of(this).add(new MaybeCreateCapacityProviderAssociations(this, id, this._ec2CapacityProviders)); + Aspects.of(this).add(new MaybeCreateCapacityProviderAssociations(this, id, this._asgCapacityProviders)); } /** @@ -201,8 +201,8 @@ export class Cluster extends Resource implements ICluster { */ public enableFargateCapacityProviders() { for (const provider of ['FARGATE', 'FARGATE_SPOT']) { - if (!this._clusterCapacityProviders.includes(provider)) { - this._clusterCapacityProviders.push(provider); + if (!this._fargateCapacityProviders.includes(provider)) { + this._fargateCapacityProviders.push(provider); } } } @@ -247,7 +247,7 @@ export class Cluster extends Resource implements ICluster { * * Returns the AutoScalingGroup so you can add autoscaling settings to it. * - * @deprecated Use {@link Cluster.addCapacityProvider} instead. + * @deprecated Use {@link Cluster.addAsgCapacityProvider} instead. */ public addCapacity(id: string, options: AddCapacityOptions): autoscaling.AutoScalingGroup { if (options.machineImage && options.machineImageType) { @@ -273,29 +273,13 @@ export class Cluster extends Resource implements ICluster { } /** - * This method adds compute capacity to a cluster using the specified EC2 Capacity Provider. + * This method adds an Auto Scaling Group Capacity Provider to a cluster. * * @param provider the capacity provider to add to this cluster. */ - public addCapacityProvider(provider: string) { - if (!(provider === 'FARGATE' || provider === 'FARGATE_SPOT')) { - throw new Error('CapacityProvider not supported'); - } - - if (!this._clusterCapacityProviders.includes(provider)) { - this._clusterCapacityProviders.push(provider); - } - } - - /** - * This method adds an Auto Scaling Group Capacity Provider to a cluster using - * the specified EC2 Capacity Provider. - * - * @param provider the capacity provider to add to this cluster. - */ - public addEC2CapacityProvider(provider: EC2CapacityProvider, options: AddAutoScalingGroupCapacityOptions = {}) { + public addAsgCapacityProvider(provider: AsgCapacityProvider, options: AddAutoScalingGroupCapacityOptions = {}) { // Don't add the same capacity provider more than once. - if (this._ec2CapacityProviders.includes(provider)) { + if (this._asgCapacityProviders.includes(provider)) { return; } @@ -306,13 +290,13 @@ export class Cluster extends Resource implements ICluster { taskDrainTime: provider.enableManagedTerminationProtection ? Duration.seconds(0) : options.taskDrainTime, }); - this._ec2CapacityProviders.push(provider); + this._asgCapacityProviders.push(provider); } /** * This method adds compute capacity to a cluster using the specified AutoScalingGroup. * - * @deprecated Use {@link Cluster.addCapacityProvider} instead. + * @deprecated Use {@link Cluster.addAsgCapacityProvider} instead. * @param autoScalingGroup the ASG to add to this cluster. * [disable-awslint:ref-via-interface] is needed in order to install the ECS * agent by updating the ASGs user data. @@ -416,6 +400,23 @@ export class Cluster extends Resource implements ICluster { } } + /** + * This method enables the Fargate or Fargate Spot capacity providers on the cluster. + * + * @param provider the capacity provider to add to this cluster. + * @deprecated Use {@link enableFargateCapacityProviders} instead. + * @see {@link addAsgCapacityProvider} to add an Auto Scaling Group capacity provider to the cluster. + */ + public addCapacityProvider(provider: string) { + if (!(provider === 'FARGATE' || provider === 'FARGATE_SPOT')) { + throw new Error('CapacityProvider not supported'); + } + + if (!this._fargateCapacityProviders.includes(provider)) { + this._fargateCapacityProviders.push(provider); + } + } + private configureWindowsAutoScalingGroup(autoScalingGroup: autoscaling.AutoScalingGroup, options: AddAutoScalingGroupCapacityOptions = {}) { // clear the cache of the agent autoScalingGroup.addUserData('Remove-Item -Recurse C:\\ProgramData\\Amazon\\ECS\\Cache'); @@ -1036,7 +1037,7 @@ enum ContainerInsights { */ export interface CapacityProviderStrategy { /** - * The name of the capacity provider + * The name of the capacity provider. */ readonly capacityProvider: string; @@ -1060,9 +1061,9 @@ capacity provider. The weight value is taken into consideration after the base v } /** - * The options for creating an EC2 Capacity Provider. + * The options for creating an Auto Scaling Group Capacity Provider. */ -export interface EC2CapacityProviderProps extends AddAutoScalingGroupCapacityOptions { +export interface AsgCapacityProviderProps extends AddAutoScalingGroupCapacityOptions { /** * The name for the capacity provider. * @@ -1112,13 +1113,13 @@ export interface EC2CapacityProviderProps extends AddAutoScalingGroupCapacityOpt } /** - * An EC2 Capacity Provider. This allows an ECS cluster to target a specific - * EC2 Auto Scaling Group for the placement of tasks. Optionally (and recommended), - * ECS can manage the number of instances in the ASG to fit the tasks, and can - * ensure that instances are not prematurely terminated while there are still tasks - * running on them. + * An Auto Scaling Group Capacity Provider. This allows an ECS cluster to target + * a specific EC2 Auto Scaling Group for the placement of tasks. Optionally (and + * recommended), ECS can manage the number of instances in the ASG to fit the + * tasks, and can ensure that instances are not prematurely terminated while + * there are still tasks running on them. */ -export class EC2CapacityProvider extends CoreConstruct { +export class AsgCapacityProvider extends CoreConstruct { /** * Capacity provider name * @default Chosen by CloudFormation @@ -1135,7 +1136,7 @@ export class EC2CapacityProvider extends CoreConstruct { */ readonly enableManagedTerminationProtection?: boolean; - constructor(scope: Construct, id: string, props: EC2CapacityProviderProps) { + constructor(scope: Construct, id: string, props: AsgCapacityProviderProps) { super(scope, id); this.autoScalingGroup = props.autoScalingGroup as autoscaling.AutoScalingGroup; @@ -1151,8 +1152,8 @@ export class EC2CapacityProvider extends CoreConstruct { name: props.capacityProviderName, autoScalingGroupProvider: { autoScalingGroupArn: this.autoScalingGroup.autoScalingGroupName, - managedScaling: { - status: props.enableManagedScaling === false ? 'DISABLED' : 'ENABLED', + managedScaling: props.enableManagedScaling === false ? undefined : { + status: 'ENABLED', targetCapacity: props.targetCapacityPercent || 100, maximumScalingStepSize: props.maximumScalingStepSize, minimumScalingStepSize: props.minimumScalingStepSize, @@ -1172,9 +1173,9 @@ export class EC2CapacityProvider extends CoreConstruct { class MaybeCreateCapacityProviderAssociations implements IAspect { private scope: CoreConstruct; private id: string; - private capacityProviders: EC2CapacityProvider[] + private capacityProviders: AsgCapacityProvider[] - constructor(scope: CoreConstruct, id: string, capacityProviders: EC2CapacityProvider[]) { + constructor(scope: CoreConstruct, id: string, capacityProviders: AsgCapacityProvider[]) { this.scope = scope; this.id = id; this.capacityProviders = capacityProviders; diff --git a/packages/@aws-cdk/aws-ecs/test/cluster.test.ts b/packages/@aws-cdk/aws-ecs/test/cluster.test.ts index 0269349948554..a76c98377c1db 100644 --- a/packages/@aws-cdk/aws-ecs/test/cluster.test.ts +++ b/packages/@aws-cdk/aws-ecs/test/cluster.test.ts @@ -4,6 +4,7 @@ import { haveResource, haveResourceLike, ResourcePart, + ABSENT, } from '@aws-cdk/assert-internal'; import * as autoscaling from '@aws-cdk/aws-autoscaling'; import * as ec2 from '@aws-cdk/aws-ec2'; @@ -1809,7 +1810,7 @@ nodeunitShim({ }); // WHEN - new ecs.EC2CapacityProvider(stack, 'provider', { + new ecs.AsgCapacityProvider(stack, 'provider', { autoScalingGroup, }); @@ -1829,7 +1830,7 @@ nodeunitShim({ test.done(); }, - 'can disable managed scaling for EC2 capacity provider'(test: Test) { + 'can disable managed scaling for ASG capacity provider'(test: Test) { // GIVEN const app = new cdk.App(); const stack = new cdk.Stack(app, 'test'); @@ -1841,7 +1842,7 @@ nodeunitShim({ }); // WHEN - new ecs.EC2CapacityProvider(stack, 'provider', { + new ecs.AsgCapacityProvider(stack, 'provider', { autoScalingGroup, enableManagedScaling: false, }); @@ -1852,10 +1853,7 @@ nodeunitShim({ AutoScalingGroupArn: { Ref: 'asgASG4D014670', }, - ManagedScaling: { - Status: 'DISABLED', - TargetCapacity: 100, - }, + ManagedScaling: ABSENT, ManagedTerminationProtection: 'ENABLED', }, })); @@ -1874,7 +1872,7 @@ nodeunitShim({ }); // WHEN - new ecs.EC2CapacityProvider(stack, 'provider', { + new ecs.AsgCapacityProvider(stack, 'provider', { autoScalingGroup, }); @@ -1897,7 +1895,7 @@ nodeunitShim({ }); // WHEN - new ecs.EC2CapacityProvider(stack, 'provider', { + new ecs.AsgCapacityProvider(stack, 'provider', { autoScalingGroup, enableManagedTerminationProtection: false, }); @@ -1909,7 +1907,7 @@ nodeunitShim({ test.done(); }, - 'can add EC2 capacity via Capacity Provider'(test: Test) { + 'can add ASG capacity via Capacity Provider'(test: Test) { // GIVEN const app = new cdk.App(); const stack = new cdk.Stack(app, 'test'); @@ -1923,7 +1921,7 @@ nodeunitShim({ }); // WHEN - const capacityProvider = new ecs.EC2CapacityProvider(stack, 'provider', { + const capacityProvider = new ecs.AsgCapacityProvider(stack, 'provider', { autoScalingGroup, enableManagedTerminationProtection: false, }); @@ -1932,8 +1930,8 @@ nodeunitShim({ cluster.enableFargateCapacityProviders(); // Ensure not added twice - cluster.addEC2CapacityProvider(capacityProvider); - cluster.addEC2CapacityProvider(capacityProvider); + cluster.addAsgCapacityProvider(capacityProvider); + cluster.addAsgCapacityProvider(capacityProvider); // THEN expect(stack).to(haveResource('AWS::ECS::ClusterCapacityProviderAssociations', { diff --git a/packages/@aws-cdk/aws-ecs/test/ec2/ec2-service.test.ts b/packages/@aws-cdk/aws-ecs/test/ec2/ec2-service.test.ts index ee0b2b9cf0de4..c63c86cce9f65 100644 --- a/packages/@aws-cdk/aws-ecs/test/ec2/ec2-service.test.ts +++ b/packages/@aws-cdk/aws-ecs/test/ec2/ec2-service.test.ts @@ -250,11 +250,11 @@ nodeunitShim({ }); // WHEN - const capacityProvider = new ecs.EC2CapacityProvider(stack, 'provider', { + const capacityProvider = new ecs.AsgCapacityProvider(stack, 'provider', { autoScalingGroup, enableManagedTerminationProtection: false, }); - cluster.addEC2CapacityProvider(capacityProvider); + cluster.addAsgCapacityProvider(capacityProvider); const taskDefinition = new ecs.TaskDefinition(stack, 'ServerTask', { compatibility: ecs.Compatibility.EC2, diff --git a/packages/@aws-cdk/aws-ecs/test/ec2/integ.capacity-provider.ts b/packages/@aws-cdk/aws-ecs/test/ec2/integ.capacity-provider.ts index 06b5f29e88437..f82ce6a9f9f56 100644 --- a/packages/@aws-cdk/aws-ecs/test/ec2/integ.capacity-provider.ts +++ b/packages/@aws-cdk/aws-ecs/test/ec2/integ.capacity-provider.ts @@ -25,11 +25,11 @@ const autoScalingGroup = new autoscaling.AutoScalingGroup(stack, 'ASG', { machineImage: ecs.EcsOptimizedImage.amazonLinux2(), }); -const cp = new ecs.EC2CapacityProvider(stack, 'EC2CapacityProvider', { +const cp = new ecs.AsgCapacityProvider(stack, 'EC2CapacityProvider', { autoScalingGroup, }); -cluster.addEC2CapacityProvider(cp); +cluster.addAsgCapacityProvider(cp); new ecs.Ec2Service(stack, 'EC2Service', { cluster, diff --git a/packages/@aws-cdk/aws-ecs/test/fargate/fargate-service.test.ts b/packages/@aws-cdk/aws-ecs/test/fargate/fargate-service.test.ts index 426f81a5e8230..a75ff256cc457 100644 --- a/packages/@aws-cdk/aws-ecs/test/fargate/fargate-service.test.ts +++ b/packages/@aws-cdk/aws-ecs/test/fargate/fargate-service.test.ts @@ -1,4 +1,4 @@ -import { expect, haveResource, haveResourceLike } from '@aws-cdk/assert-internal'; +import { expect, haveResource, haveResourceLike, ABSENT } from '@aws-cdk/assert-internal'; import * as appscaling from '@aws-cdk/aws-applicationautoscaling'; import * as cloudwatch from '@aws-cdk/aws-cloudwatch'; import * as ec2 from '@aws-cdk/aws-ec2'; @@ -244,6 +244,7 @@ nodeunitShim({ MinimumHealthyPercent: 50, }, // no launch type + LaunchType: ABSENT, CapacityProviderStrategy: [ { CapacityProvider: 'FARGATE_SPOT',