Skip to content

Commit 89802a9

Browse files
authored
fix(ecs): validate ecs healthcheck (#24197)
---- #22200 I add a feature to validate some contents for healthcheck. ### All Submissions: * [x] Have you followed the guidelines in our [Contributing guide?](https://github.com/aws/aws-cdk/blob/main/CONTRIBUTING.md) ### Adding new Unconventional Dependencies: * [ ] This PR adds new unconventional dependencies following the process described [here](https://github.com/aws/aws-cdk/blob/main/CONTRIBUTING.md/#adding-new-unconventional-dependencies) ### New Features * [ ] Have you added the new feature to an [integration test](https://github.com/aws/aws-cdk/blob/main/INTEGRATION_TESTS.md)? * [ ] Did you use `yarn integ` to deploy the infrastructure and generate the snapshot (i.e. `yarn integ` without `--dry-run`)? *By submitting this pull request, I confirm that my contribution is made under the terms of the Apache-2.0 license*
1 parent f61d950 commit 89802a9

11 files changed

+438
-41
lines changed

packages/@aws-cdk/aws-ecs/lib/container-definition.ts

+17
Original file line numberDiff line numberDiff line change
@@ -858,6 +858,23 @@ function renderEnvironmentFiles(partition: string, environmentFiles: Environment
858858
}
859859

860860
function renderHealthCheck(hc: HealthCheck): CfnTaskDefinition.HealthCheckProperty {
861+
if (hc.interval?.toSeconds() !== undefined) {
862+
if (5 > hc.interval?.toSeconds() || hc.interval?.toSeconds() > 300) {
863+
throw new Error('Interval must be between 5 seconds and 300 seconds.');
864+
}
865+
}
866+
867+
if (hc.timeout?.toSeconds() !== undefined) {
868+
if (2 > hc.timeout?.toSeconds() || hc.timeout?.toSeconds() > 120) {
869+
throw new Error('Timeout must be between 2 seconds and 120 seconds.');
870+
}
871+
}
872+
if (hc.interval?.toSeconds() !== undefined && hc.timeout?.toSeconds() !== undefined) {
873+
if (hc.interval?.toSeconds() < hc.timeout?.toSeconds()) {
874+
throw new Error('Health check interval should be longer than timeout.');
875+
}
876+
}
877+
861878
return {
862879
command: getHealthCheckCommand(hc),
863880
interval: hc.interval?.toSeconds() ?? 30,

packages/@aws-cdk/aws-ecs/test/container-definition.test.ts

+156
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import * as cdk from '@aws-cdk/core';
77
import * as cxapi from '@aws-cdk/cx-api';
88
import * as ecs from '../lib';
99
import { AppProtocol } from '../lib';
10+
import { Duration } from '@aws-cdk/core';
1011

1112
describe('container definition', () => {
1213
describe('When creating a Task Definition', () => {
@@ -1691,6 +1692,161 @@ describe('container definition', () => {
16911692
}).toThrow(/At least one argument must be supplied for health check command./);
16921693
});
16931694

1695+
test('throws when setting Health Check with invalid interval because of too short', () => {
1696+
// GIVEN
1697+
const stack = new cdk.Stack();
1698+
const taskDefinition = new ecs.Ec2TaskDefinition(stack, 'TaskDef');
1699+
1700+
// WHEN
1701+
taskDefinition.addContainer('cont', {
1702+
image: ecs.ContainerImage.fromRegistry('test'),
1703+
memoryLimitMiB: 1024,
1704+
healthCheck: {
1705+
command: ['CMD-SHELL', 'curl localhost:8000'],
1706+
interval: Duration.seconds(4),
1707+
timeout: Duration.seconds(30),
1708+
},
1709+
});
1710+
1711+
// THEN
1712+
expect(() => {
1713+
Template.fromStack(stack).hasResourceProperties('AWS::ECS::TaskDefinition', {
1714+
ContainerDefinitions: [
1715+
{
1716+
HealthCheck: {
1717+
Command: ['CMD-SHELL', 'curl localhost:8000'],
1718+
Interval: 4,
1719+
},
1720+
},
1721+
],
1722+
});
1723+
}).toThrow(/Interval must be between 5 seconds and 300 seconds./);
1724+
});
1725+
1726+
test('throws when setting Health Check with invalid interval because of too long', () => {
1727+
// GIVEN
1728+
const stack = new cdk.Stack();
1729+
const taskDefinition = new ecs.Ec2TaskDefinition(stack, 'TaskDef');
1730+
1731+
// WHEN
1732+
taskDefinition.addContainer('cont', {
1733+
image: ecs.ContainerImage.fromRegistry('test'),
1734+
memoryLimitMiB: 1024,
1735+
healthCheck: {
1736+
command: ['CMD-SHELL', 'curl localhost:8000'],
1737+
interval: Duration.seconds(301),
1738+
timeout: Duration.seconds(30),
1739+
},
1740+
});
1741+
1742+
// THEN
1743+
expect(() => {
1744+
Template.fromStack(stack).hasResourceProperties('AWS::ECS::TaskDefinition', {
1745+
ContainerDefinitions: [
1746+
{
1747+
HealthCheck: {
1748+
Command: ['CMD-SHELL', 'curl localhost:8000'],
1749+
Interval: 4,
1750+
},
1751+
},
1752+
],
1753+
});
1754+
}).toThrow(/Interval must be between 5 seconds and 300 seconds./);
1755+
});
1756+
1757+
test('throws when setting Health Check with invalid timeout because of too short', () => {
1758+
// GIVEN
1759+
const stack = new cdk.Stack();
1760+
const taskDefinition = new ecs.Ec2TaskDefinition(stack, 'TaskDef');
1761+
1762+
// WHEN
1763+
taskDefinition.addContainer('cont', {
1764+
image: ecs.ContainerImage.fromRegistry('test'),
1765+
memoryLimitMiB: 1024,
1766+
healthCheck: {
1767+
command: ['CMD-SHELL', 'curl localhost:8000'],
1768+
interval: Duration.seconds(40),
1769+
timeout: Duration.seconds(1),
1770+
},
1771+
});
1772+
1773+
// THEN
1774+
expect(() => {
1775+
Template.fromStack(stack).hasResourceProperties('AWS::ECS::TaskDefinition', {
1776+
ContainerDefinitions: [
1777+
{
1778+
HealthCheck: {
1779+
Command: ['CMD-SHELL', 'curl localhost:8000'],
1780+
Interval: 4,
1781+
},
1782+
},
1783+
],
1784+
});
1785+
}).toThrow(/Timeout must be between 2 seconds and 120 seconds./);
1786+
});
1787+
1788+
test('throws when setting Health Check with invalid timeout because of too long', () => {
1789+
// GIVEN
1790+
const stack = new cdk.Stack();
1791+
const taskDefinition = new ecs.Ec2TaskDefinition(stack, 'TaskDef');
1792+
1793+
// WHEN
1794+
taskDefinition.addContainer('cont', {
1795+
image: ecs.ContainerImage.fromRegistry('test'),
1796+
memoryLimitMiB: 1024,
1797+
healthCheck: {
1798+
command: ['CMD-SHELL', 'curl localhost:8000'],
1799+
interval: Duration.seconds(150),
1800+
timeout: Duration.seconds(130),
1801+
},
1802+
});
1803+
1804+
// THEN
1805+
expect(() => {
1806+
Template.fromStack(stack).hasResourceProperties('AWS::ECS::TaskDefinition', {
1807+
ContainerDefinitions: [
1808+
{
1809+
HealthCheck: {
1810+
Command: ['CMD-SHELL', 'curl localhost:8000'],
1811+
Interval: 4,
1812+
},
1813+
},
1814+
],
1815+
});
1816+
}).toThrow(/Timeout must be between 2 seconds and 120 seconds./);
1817+
});
1818+
1819+
test('throws when setting Health Check with invalid interval and timeout because timeout is longer than interval', () => {
1820+
// GIVEN
1821+
const stack = new cdk.Stack();
1822+
const taskDefinition = new ecs.Ec2TaskDefinition(stack, 'TaskDef');
1823+
1824+
// WHEN
1825+
taskDefinition.addContainer('cont', {
1826+
image: ecs.ContainerImage.fromRegistry('test'),
1827+
memoryLimitMiB: 1024,
1828+
healthCheck: {
1829+
command: ['CMD-SHELL', 'curl localhost:8000'],
1830+
interval: Duration.seconds(10),
1831+
timeout: Duration.seconds(30),
1832+
},
1833+
});
1834+
1835+
// THEN
1836+
expect(() => {
1837+
Template.fromStack(stack).hasResourceProperties('AWS::ECS::TaskDefinition', {
1838+
ContainerDefinitions: [
1839+
{
1840+
HealthCheck: {
1841+
Command: ['CMD-SHELL', 'curl localhost:8000'],
1842+
Interval: 4,
1843+
},
1844+
},
1845+
],
1846+
});
1847+
}).toThrow(/Health check interval should be longer than timeout./);
1848+
});
1849+
16941850
test('can specify Health Check values in shell form', () => {
16951851
// GIVEN
16961852
const stack = new cdk.Stack();

packages/@aws-cdk/aws-ecs/test/fargate/integ.exec-command.js.snapshot/aws-ecs-integ-exec-command.assets.json

+3-3
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,15 @@
11
{
2-
"version": "20.0.0",
2+
"version": "30.0.0",
33
"files": {
4-
"4dba456b46dc53b954d12cf55bad7b455371f307d7b5df57b5fb2e6cafe4e9ba": {
4+
"1a5bcacf8adc1fb93503daec527cf36ecf57f189012726acf8ad69d9f993d3cb": {
55
"source": {
66
"path": "aws-ecs-integ-exec-command.template.json",
77
"packaging": "file"
88
},
99
"destinations": {
1010
"current_account-current_region": {
1111
"bucketName": "cdk-hnb659fds-assets-${AWS::AccountId}-${AWS::Region}",
12-
"objectKey": "4dba456b46dc53b954d12cf55bad7b455371f307d7b5df57b5fb2e6cafe4e9ba.json",
12+
"objectKey": "1a5bcacf8adc1fb93503daec527cf36ecf57f189012726acf8ad69d9f993d3cb.json",
1313
"assumeRoleArn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-file-publishing-role-${AWS::AccountId}-${AWS::Region}"
1414
}
1515
}

packages/@aws-cdk/aws-ecs/test/fargate/integ.exec-command.js.snapshot/aws-ecs-integ-exec-command.template.json

+9
Original file line numberDiff line numberDiff line change
@@ -673,6 +673,15 @@
673673
"ContainerDefinitions": [
674674
{
675675
"Essential": true,
676+
"HealthCheck": {
677+
"Command": [
678+
"CMD-SHELL",
679+
"curl localhost:8000"
680+
],
681+
"Interval": 60,
682+
"Retries": 3,
683+
"Timeout": 40
684+
},
676685
"Image": "amazon/amazon-ecs-sample",
677686
"Name": "web"
678687
}
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
{"version":"20.0.0"}
1+
{"version":"30.0.0"}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
{
2+
"version": "30.0.0",
3+
"files": {
4+
"21fbb51d7b23f6a6c262b46a9caee79d744a3ac019fd45422d988b96d44b2a22": {
5+
"source": {
6+
"path": "execcommandintegtestDefaultTestDeployAssert4F7706FE.template.json",
7+
"packaging": "file"
8+
},
9+
"destinations": {
10+
"current_account-current_region": {
11+
"bucketName": "cdk-hnb659fds-assets-${AWS::AccountId}-${AWS::Region}",
12+
"objectKey": "21fbb51d7b23f6a6c262b46a9caee79d744a3ac019fd45422d988b96d44b2a22.json",
13+
"assumeRoleArn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-file-publishing-role-${AWS::AccountId}-${AWS::Region}"
14+
}
15+
}
16+
}
17+
},
18+
"dockerImages": {}
19+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
{
2+
"Parameters": {
3+
"BootstrapVersion": {
4+
"Type": "AWS::SSM::Parameter::Value<String>",
5+
"Default": "/cdk-bootstrap/hnb659fds/version",
6+
"Description": "Version of the CDK Bootstrap resources in this environment, automatically retrieved from SSM Parameter Store. [cdk:skip]"
7+
}
8+
},
9+
"Rules": {
10+
"CheckBootstrapVersion": {
11+
"Assertions": [
12+
{
13+
"Assert": {
14+
"Fn::Not": [
15+
{
16+
"Fn::Contains": [
17+
[
18+
"1",
19+
"2",
20+
"3",
21+
"4",
22+
"5"
23+
],
24+
{
25+
"Ref": "BootstrapVersion"
26+
}
27+
]
28+
}
29+
]
30+
},
31+
"AssertDescription": "CDK bootstrap stack version 6 required. Please run 'cdk bootstrap' with a recent version of the CDK CLI."
32+
}
33+
]
34+
}
35+
}
36+
}
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,20 @@
11
{
2-
"version": "20.0.0",
2+
"version": "30.0.0",
33
"testCases": {
4-
"integ.exec-command": {
4+
"exec-command-integ-test/DefaultTest": {
55
"stacks": [
66
"aws-ecs-integ-exec-command"
77
],
8-
"diffAssets": false,
9-
"stackUpdateWorkflow": true
8+
"diffAssets": true,
9+
"cdkCommandOptions": {
10+
"deploy": {
11+
"args": {
12+
"rollback": true
13+
}
14+
}
15+
},
16+
"assertionStack": "exec-command-integ-test/DefaultTest/DeployAssert",
17+
"assertionStackName": "execcommandintegtestDefaultTestDeployAssert4F7706FE"
1018
}
11-
},
12-
"synthContext": {},
13-
"enableLookups": false
19+
}
1420
}

0 commit comments

Comments
 (0)