Skip to content

Commit aca68ba

Browse files
authored
fix(ecs): stack name can result in noncompliant capacity provider name (#29235)
Closes #29151. ---- *By submitting this pull request, I confirm that my contribution is made under the terms of the Apache-2.0 license*
1 parent 3cbad4a commit aca68ba

File tree

2 files changed

+42
-6
lines changed

2 files changed

+42
-6
lines changed

packages/aws-cdk-lib/aws-ecs/lib/cluster.ts

Lines changed: 16 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ import * as kms from '../../aws-kms';
1111
import * as logs from '../../aws-logs';
1212
import * as s3 from '../../aws-s3';
1313
import * as cloudmap from '../../aws-servicediscovery';
14-
import { Duration, IResource, Resource, Stack, Aspects, ArnFormat, IAspect, Token } from '../../core';
14+
import { Duration, IResource, Resource, Stack, Aspects, ArnFormat, IAspect, Token, Names } from '../../core';
1515

1616
const CLUSTER_SYMBOL = Symbol.for('@aws-cdk/aws-ecs/lib/cluster.Cluster');
1717

@@ -1168,6 +1168,8 @@ export interface AsgCapacityProviderProps extends AddAutoScalingGroupCapacityOpt
11681168
* The name of the capacity provider. If a name is specified,
11691169
* it cannot start with `aws`, `ecs`, or `fargate`. If no name is specified,
11701170
* a default name in the CFNStackName-CFNResourceName-RandomString format is used.
1171+
* If the stack name starts with `aws`, `ecs`, or `fargate`, a unique resource name
1172+
* is generated that starts with `cp-`.
11711173
*
11721174
* @default CloudFormation-generated name
11731175
*/
@@ -1288,6 +1290,7 @@ export class AsgCapacityProvider extends Construct {
12881290

12891291
constructor(scope: Construct, id: string, props: AsgCapacityProviderProps) {
12901292
super(scope, id);
1293+
let capacityProviderName = props.capacityProviderName;
12911294
this.autoScalingGroup = props.autoScalingGroup as autoscaling.AutoScalingGroup;
12921295
this.machineImageType = props.machineImageType ?? MachineImageType.AMAZON_LINUX_2;
12931296
this.canContainersAccessInstanceRole = props.canContainersAccessInstanceRole;
@@ -1306,9 +1309,17 @@ export class AsgCapacityProvider extends Construct {
13061309
this.autoScalingGroup.protectNewInstancesFromScaleIn();
13071310
}
13081311

1309-
if (props.capacityProviderName) {
1310-
if (!(/^(?!aws|ecs|fargate).+/gm.test(props.capacityProviderName))) {
1311-
throw new Error(`Invalid Capacity Provider Name: ${props.capacityProviderName}, If a name is specified, it cannot start with aws, ecs, or fargate.`);
1312+
const capacityProviderNameRegex = /^(?!aws|ecs|fargate).+/gm;
1313+
if (capacityProviderName) {
1314+
if (!(capacityProviderNameRegex.test(capacityProviderName))) {
1315+
throw new Error(`Invalid Capacity Provider Name: ${capacityProviderName}, If a name is specified, it cannot start with aws, ecs, or fargate.`);
1316+
}
1317+
} else {
1318+
if (!(capacityProviderNameRegex.test(Stack.of(this).stackName))) {
1319+
// name cannot start with 'aws|ecs|fargate', so append 'cp-'
1320+
// 255 is the max length, subtract 3 because of 'cp-'
1321+
// if the regex condition isn't met, CFN will name the capacity provider
1322+
capacityProviderName = 'cp-' + Names.uniqueResourceName(this, { maxLength: 252, allowedSpecialCharacters: '-_' });
13121323
}
13131324
}
13141325

@@ -1319,7 +1330,7 @@ export class AsgCapacityProvider extends Construct {
13191330
}
13201331

13211332
const capacityProvider = new CfnCapacityProvider(this, id, {
1322-
name: props.capacityProviderName,
1333+
name: capacityProviderName,
13231334
autoScalingGroupProvider: {
13241335
autoScalingGroupArn: this.autoScalingGroup.autoScalingGroupName,
13251336
managedScaling: props.enableManagedScaling === false ? undefined : {

packages/aws-cdk-lib/aws-ecs/test/cluster.test.ts

Lines changed: 26 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2928,7 +2928,7 @@ test('can add ASG capacity via Capacity Provider by not specifying machineImageT
29282928

29292929
});
29302930

2931-
test('throws when ASG Capacity Provider with capacityProviderName starting with aws, ecs or faragte', () => {
2931+
test('throws when ASG Capacity Provider with capacityProviderName starting with aws, ecs or fargate', () => {
29322932
// GIVEN
29332933
const app = new cdk.App();
29342934
const stack = new cdk.Stack(app, 'test');
@@ -2965,6 +2965,31 @@ test('throws when ASG Capacity Provider with capacityProviderName starting with
29652965
}).toThrow(/Invalid Capacity Provider Name: ecscp, If a name is specified, it cannot start with aws, ecs, or fargate./);
29662966
});
29672967

2968+
test('throws when ASG Capacity Provider with no capacityProviderName but stack name starting with aws, ecs or fargate', () => {
2969+
// GIVEN
2970+
const app = new cdk.App();
2971+
const stack = new cdk.Stack(app, 'ecscp');
2972+
const vpc = new ec2.Vpc(stack, 'Vpc');
2973+
const cluster = new ecs.Cluster(stack, 'EcsCluster');
2974+
2975+
const autoScalingGroupAl2 = new autoscaling.AutoScalingGroup(stack, 'asgal2', {
2976+
vpc,
2977+
instanceType: new ec2.InstanceType('bogus'),
2978+
machineImage: ecs.EcsOptimizedImage.amazonLinux2(),
2979+
});
2980+
2981+
expect(() => {
2982+
// WHEN Capacity Provider when stack name starts with ecs.
2983+
const capacityProvider = new ecs.AsgCapacityProvider(stack, 'provideral2-2', {
2984+
autoScalingGroup: autoScalingGroupAl2,
2985+
enableManagedTerminationProtection: false,
2986+
});
2987+
2988+
cluster.addAsgCapacityProvider(capacityProvider);
2989+
2990+
}).not.toThrow();
2991+
});
2992+
29682993
test('throws when InstanceWarmupPeriod is less than 0', () => {
29692994
// GIVEN
29702995
const app = new cdk.App();

0 commit comments

Comments
 (0)