Skip to content

Commit bdfdc91

Browse files
authored
fix(ecs-patterns): minHealthyPercent and maxHealthyPercent props validation (#26193)
Setting `maxHealthyPercent` to a non-integer value was not raising synth-time errors, but was generating invalid CFN templates. This fix adds validation for both `maxHealthyPercent` and `minHealthyPercent`. Closes #26158. ---- *By submitting this pull request, I confirm that my contribution is made under the terms of the Apache-2.0 license*
1 parent 0e808d8 commit bdfdc91

File tree

2 files changed

+76
-1
lines changed

2 files changed

+76
-1
lines changed

packages/aws-cdk-lib/aws-ecs-patterns/lib/fargate/application-load-balanced-fargate-service.ts

+24-1
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import { Construct } from 'constructs';
22
import { ISecurityGroup, SubnetSelection } from '../../../aws-ec2';
33
import { FargateService, FargateTaskDefinition } from '../../../aws-ecs';
4-
import { FeatureFlags } from '../../../core';
4+
import { FeatureFlags, Token } from '../../../core';
55
import * as cxapi from '../../../cx-api';
66
import { ApplicationLoadBalancedServiceBase, ApplicationLoadBalancedServiceBaseProps } from '../base/application-load-balanced-service-base';
77
import { FargateServiceBaseProps } from '../base/fargate-service-base';
@@ -96,6 +96,19 @@ export class ApplicationLoadBalancedFargateService extends ApplicationLoadBalanc
9696
throw new Error('You must specify one of: taskDefinition or image');
9797
}
9898

99+
this.validateHealthyPercentage('minHealthyPercent', props.minHealthyPercent);
100+
this.validateHealthyPercentage('maxHealthyPercent', props.maxHealthyPercent);
101+
102+
if (
103+
props.minHealthyPercent &&
104+
!Token.isUnresolved(props.minHealthyPercent) &&
105+
props.maxHealthyPercent &&
106+
!Token.isUnresolved(props.maxHealthyPercent) &&
107+
props.minHealthyPercent >= props.maxHealthyPercent
108+
) {
109+
throw new Error('Minimum healthy percent must be less than maximum healthy percent.');
110+
}
111+
99112
const desiredCount = FeatureFlags.of(this).isEnabled(cxapi.ECS_REMOVE_DEFAULT_DESIRED_COUNT) ? this.internalDesiredCount : this.desiredCount;
100113

101114
this.service = new FargateService(this, 'Service', {
@@ -120,4 +133,14 @@ export class ApplicationLoadBalancedFargateService extends ApplicationLoadBalanc
120133
});
121134
this.addServiceAsTarget(this.service);
122135
}
136+
137+
/**
138+
* Throws an error if the specified percent is not an integer or negative.
139+
*/
140+
private validateHealthyPercentage(name: string, value?: number) {
141+
if (value === undefined || Token.isUnresolved(value)) { return; }
142+
if (!Number.isInteger(value) || value < 0) {
143+
throw new Error(`${name}: Must be a non-negative integer; received ${value}`);
144+
}
145+
}
123146
}

packages/aws-cdk-lib/aws-ecs-patterns/test/fargate/load-balanced-fargate-service.test.ts

+52
Original file line numberDiff line numberDiff line change
@@ -1208,3 +1208,55 @@ test('NetworkLoadBalancedFargateService multiple capacity provider strategies ar
12081208
]),
12091209
});
12101210
});
1211+
1212+
test('should validate minHealthyPercent', () => {
1213+
// GIVEN
1214+
const stack = new cdk.Stack();
1215+
const vpc = new ec2.Vpc(stack, 'VPC');
1216+
const cluster = new ecs.Cluster(stack, 'Cluster', { vpc });
1217+
1218+
// WHEN
1219+
expect(() => new ecsPatterns.ApplicationLoadBalancedFargateService(stack, 'Service', {
1220+
cluster,
1221+
taskImageOptions: {
1222+
image: ecs.ContainerImage.fromRegistry('/aws/aws-example-app'),
1223+
dockerLabels: { label1: 'labelValue1', label2: 'labelValue2' },
1224+
},
1225+
minHealthyPercent: 0.5,
1226+
})).toThrow(/Must be a non-negative integer/);
1227+
});
1228+
1229+
test('should validate maxHealthyPercent', () => {
1230+
// GIVEN
1231+
const stack = new cdk.Stack();
1232+
const vpc = new ec2.Vpc(stack, 'VPC');
1233+
const cluster = new ecs.Cluster(stack, 'Cluster', { vpc });
1234+
1235+
// WHEN
1236+
expect(() => new ecsPatterns.ApplicationLoadBalancedFargateService(stack, 'Service', {
1237+
cluster,
1238+
taskImageOptions: {
1239+
image: ecs.ContainerImage.fromRegistry('/aws/aws-example-app'),
1240+
dockerLabels: { label1: 'labelValue1', label2: 'labelValue2' },
1241+
},
1242+
maxHealthyPercent: 0.5,
1243+
})).toThrow(/Must be a non-negative integer/);
1244+
});
1245+
1246+
test('minHealthyPercent must be less than maxHealthyPercent', () => {
1247+
// GIVEN
1248+
const stack = new cdk.Stack();
1249+
const vpc = new ec2.Vpc(stack, 'VPC');
1250+
const cluster = new ecs.Cluster(stack, 'Cluster', { vpc });
1251+
1252+
// WHEN
1253+
expect(() => new ecsPatterns.ApplicationLoadBalancedFargateService(stack, 'Service', {
1254+
cluster,
1255+
taskImageOptions: {
1256+
image: ecs.ContainerImage.fromRegistry('/aws/aws-example-app'),
1257+
dockerLabels: { label1: 'labelValue1', label2: 'labelValue2' },
1258+
},
1259+
minHealthyPercent: 100,
1260+
maxHealthyPercent: 70,
1261+
})).toThrow(/must be less than/);
1262+
});

0 commit comments

Comments
 (0)