Skip to content

Commit c0eeb83

Browse files
committed
feat(ec2): support throughput on LaunchTemplate EBS volumes
This support was simply not included in ec2 when it was added to autoscaling in aws#22441. I have copied that PR's implementation implementation to ec2 and similarly adapted its tests. Fixes aws#24341.
1 parent 9a5d4f0 commit c0eeb83

File tree

9 files changed

+125
-7
lines changed

9 files changed

+125
-7
lines changed

packages/@aws-cdk-testing/framework-integ/test/aws-ec2/test/integ.launch-template.js.snapshot/aws-cdk-ec2-lt-metadata-1.assets.json

+2-2
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

packages/@aws-cdk-testing/framework-integ/test/aws-ec2/test/integ.launch-template.js.snapshot/aws-cdk-ec2-lt-metadata-1.template.json

+10
Original file line numberDiff line numberDiff line change
@@ -159,6 +159,16 @@
159159
"Type": "AWS::EC2::LaunchTemplate",
160160
"Properties": {
161161
"LaunchTemplateData": {
162+
"BlockDeviceMappings": [
163+
{
164+
"DeviceName": "/dev/xvda",
165+
"Ebs": {
166+
"Throughput": 250,
167+
"VolumeSize": 15,
168+
"VolumeType": "gp3"
169+
}
170+
}
171+
],
162172
"MetadataOptions": {
163173
"HttpEndpoint": "enabled",
164174
"HttpProtocolIpv6": "enabled",

packages/@aws-cdk-testing/framework-integ/test/aws-ec2/test/integ.launch-template.js.snapshot/manifest.json

+1-1
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

packages/@aws-cdk-testing/framework-integ/test/aws-ec2/test/integ.launch-template.js.snapshot/tree.json

+10
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

packages/@aws-cdk-testing/framework-integ/test/aws-ec2/test/integ.launch-template.ts

+7
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,13 @@ const lt = new ec2.LaunchTemplate(stack, 'LT', {
2222
httpTokens: ec2.LaunchTemplateHttpTokens.REQUIRED,
2323
instanceMetadataTags: true,
2424
securityGroup: sg1,
25+
blockDevices: [{
26+
deviceName: '/dev/xvda',
27+
volume: ec2.BlockDeviceVolume.ebs(15, {
28+
volumeType: ec2.EbsDeviceVolumeType.GP3,
29+
throughput: 250,
30+
}),
31+
}],
2532
});
2633

2734
const sg2 = new ec2.SecurityGroup(stack, 'sg2', {

packages/aws-cdk-lib/aws-ec2/README.md

+6-3
Original file line numberDiff line numberDiff line change
@@ -1543,8 +1543,8 @@ new ec2.Instance(this, 'Instance', {
15431543
### Block Devices
15441544

15451545
To add EBS block device mappings, specify the `blockDevices` property. The following example sets the EBS-backed
1546-
root device (`/dev/sda1`) size to 50 GiB, and adds another EBS-backed device mapped to `/dev/sdm` that is 100 GiB in
1547-
size:
1546+
root device (`/dev/sda1`) size to 50 GiB, and adds another EBS-backed GP3 device mapped to `/dev/sdm` that is 100 GiB in
1547+
size, with the GP3-specific `throughput` option set to 250 MiB/s:
15481548

15491549
```ts
15501550
declare const vpc: ec2.Vpc;
@@ -1565,7 +1565,10 @@ new ec2.Instance(this, 'Instance', {
15651565
},
15661566
{
15671567
deviceName: '/dev/sdm',
1568-
volume: ec2.BlockDeviceVolume.ebs(100),
1568+
volume: ec2.BlockDeviceVolume.ebs(100, {
1569+
volumeType: ec2.EbsDeviceVolumeType.GP3,
1570+
throughput: 250,
1571+
}),
15691572
},
15701573
],
15711574
});

packages/aws-cdk-lib/aws-ec2/lib/private/ebs-util.ts

+25-1
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,30 @@ function synthesizeBlockDeviceMappings<RT, NDT>(construct: Construct, blockDevic
2525

2626
if (ebs) {
2727

28-
const { iops, volumeType, kmsKey, ...rest } = ebs;
28+
const { iops, throughput, volumeType, kmsKey, ...rest } = ebs;
29+
30+
if (throughput) {
31+
const throughputRange = { Min: 125, Max: 1000 };
32+
const { Min, Max } = throughputRange;
33+
34+
if (volumeType != EbsDeviceVolumeType.GP3) {
35+
throw new Error('throughput property requires volumeType: EbsDeviceVolumeType.GP3');
36+
}
37+
38+
if (throughput < Min || throughput > Max) {
39+
throw new Error(
40+
`throughput property takes a minimum of ${Min} and a maximum of ${Max}`,
41+
);
42+
}
43+
44+
const maximumThroughputRatio = 0.25;
45+
if (iops) {
46+
const iopsRatio = (throughput / iops);
47+
if (iopsRatio > maximumThroughputRatio) {
48+
throw new Error(`Throughput (MiBps) to iops ratio of ${iopsRatio} is too high; maximum is ${maximumThroughputRatio} MiBps per iops`);
49+
}
50+
}
51+
}
2952

3053
if (!iops) {
3154
if (volumeType === EbsDeviceVolumeType.IO1 || volumeType === EbsDeviceVolumeType.IO2) {
@@ -43,6 +66,7 @@ function synthesizeBlockDeviceMappings<RT, NDT>(construct: Construct, blockDevic
4366
finalEbs = {
4467
...rest,
4568
iops,
69+
throughput,
4670
volumeType,
4771
kmsKeyId: kmsKey?.keyArn,
4872
};

packages/aws-cdk-lib/aws-ec2/lib/volume.ts

+8
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,14 @@ export interface EbsDeviceOptionsBase {
7070
* `@aws-cdk/aws-ec2:ebsDefaultGp3Volume` is enabled.
7171
*/
7272
readonly volumeType?: EbsDeviceVolumeType;
73+
74+
/**
75+
* The throughput that the volume supports, in MiB/s
76+
* Takes a minimum of 125 and maximum of 1000.
77+
* @see https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/EBSVolumeTypes.html
78+
* @default - 125 MiB/s. Only valid on gp3 volumes.
79+
*/
80+
readonly throughput?: number;
7381
}
7482

7583
/**

packages/aws-cdk-lib/aws-ec2/test/launch-template.test.ts

+56
Original file line numberDiff line numberDiff line change
@@ -314,6 +314,12 @@ describe('LaunchTemplate', () => {
314314
}, {
315315
deviceName: 'ephemeral',
316316
volume: BlockDeviceVolume.ephemeral(0),
317+
}, {
318+
deviceName: 'gp3-with-throughput',
319+
volume: BlockDeviceVolume.ebs(15, {
320+
volumeType: EbsDeviceVolumeType.GP3,
321+
throughput: 350,
322+
}),
317323
},
318324
];
319325

@@ -366,10 +372,60 @@ describe('LaunchTemplate', () => {
366372
DeviceName: 'ephemeral',
367373
VirtualName: 'ephemeral0',
368374
},
375+
{
376+
DeviceName: 'gp3-with-throughput',
377+
Ebs: {
378+
VolumeSize: 15,
379+
VolumeType: 'gp3',
380+
Throughput: 350,
381+
},
382+
},
369383
],
370384
},
371385
});
372386
});
387+
test.each([124, 1001])('throws if throughput is set less than 125 or more than 1000', (throughput) => {
388+
expect(() => {
389+
new LaunchTemplate(stack, 'LaunchTemplate', {
390+
blockDevices: [{
391+
deviceName: 'ebs',
392+
volume: BlockDeviceVolume.ebs(15, {
393+
volumeType: EbsDeviceVolumeType.GP3,
394+
throughput,
395+
}),
396+
}],
397+
});
398+
}).toThrow(/throughput property takes a minimum of 125 and a maximum of 1000/);
399+
});
400+
test.each([
401+
...Object.values(EbsDeviceVolumeType).filter((v) => v !== 'gp3'),
402+
])('throws if throughput is set on any volume type other than GP3', (volumeType) => {
403+
expect(() => {
404+
new LaunchTemplate(stack, 'LaunchTemplate', {
405+
blockDevices: [{
406+
deviceName: 'ebs',
407+
volume: BlockDeviceVolume.ebs(15, {
408+
volumeType: volumeType,
409+
throughput: 150,
410+
}),
411+
}],
412+
});
413+
}).toThrow(/throughput property requires volumeType: EbsDeviceVolumeType.GP3/);
414+
});
415+
test('throws if throughput / iops ratio is greater than 0.25', () => {
416+
expect(() => {
417+
new LaunchTemplate(stack, 'LaunchTemplate', {
418+
blockDevices: [{
419+
deviceName: 'ebs',
420+
volume: BlockDeviceVolume.ebs(15, {
421+
volumeType: EbsDeviceVolumeType.GP3,
422+
throughput: 751,
423+
iops: 3000,
424+
}),
425+
}],
426+
});
427+
}).toThrow('Throughput (MiBps) to iops ratio of 0.25033333333333335 is too high; maximum is 0.25 MiBps per iops');
428+
});
373429

374430
test('Given instance profile', () => {
375431
// GIVEN

0 commit comments

Comments
 (0)