Skip to content

Commit

Permalink
feat(ec2): support reserving IP space in VPCs (#2090)
Browse files Browse the repository at this point in the history
SubnetConfiguration takes a new argument `reserved: true` which can be 
used to allocate IP space in the VPC without actually instantiating the
VPC resources.
  • Loading branch information
darcoli authored and rix0rrr committed Apr 1, 2019
1 parent fb9fef2 commit 4819ff4
Show file tree
Hide file tree
Showing 3 changed files with 133 additions and 1 deletion.
46 changes: 46 additions & 0 deletions packages/@aws-cdk/aws-ec2/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -172,6 +172,52 @@ The `VpcNetwork` above will have the exact same subnet definitions as listed
above. However, this time the VPC will have only 1 NAT Gateway and all
Application subnets will route to the NAT Gateway.

#### Reserving subnet IP space
There are situations where the IP space for a subnet or number of subnets
will need to be reserved. This is useful in situations where subnets
would need to be added after the vpc is originally deployed, without causing
IP renumbering for existing subnets. The IP space for a subnet may be reserved
by setting the `reserved` subnetConfiguration property to true, as shown below:

```ts
import ec2 = require('@aws-cdk/aws-ec2');
const vpc = new ec2.VpcNetwork(this, 'TheVPC', {
cidr: '10.0.0.0/16',
natGateways: 1,
subnetConfiguration: [
{
cidrMask: 26,
name: 'Public',
subnetType: SubnetType.Public,
},
{
cidrMask: 26,
name: 'Application1',
subnetType: SubnetType.Private,
},
{
cidrMask: 26,
name: 'Application2',
subnetType: SubnetType.Private,
reserved: true,
},
{
cidrMask: 27,
name: 'Database',
subnetType: SubnetType.Isolated,
}
],
});
```

In the example above, the subnet for Application2 is not actually provisioned
but its IP space is still reserved. If in the future this subnet needs to be
provisioned, then the `reserved: true` property should be removed. Most
importantly, this action would not cause the Database subnet to get renumbered,
but rather the IP space that was previously reserved will be used for the
subnet provisioned for Application2. The `reserved` property also takes into
consideration the number of availability zones when reserving IP space.

#### Sharing VPCs between stacks

If you are creating multiple `Stack`s inside the same CDK application, you
Expand Down
20 changes: 19 additions & 1 deletion packages/@aws-cdk/aws-ec2/lib/vpc.ts
Original file line number Diff line number Diff line change
Expand Up @@ -183,10 +183,22 @@ export interface SubnetConfiguration {
/**
* The common Logical Name for the `VpcSubnet`
*
* Thi name will be suffixed with an integer correlating to a specific
* This name will be suffixed with an integer correlating to a specific
* availability zone.
*/
readonly name: string;

/**
* Controls if subnet IP space needs to be reserved.
*
* When true, the IP space for the subnet is reserved but no actual
* resources are provisioned. This space is only dependent on the
* number of availibility zones and on `cidrMask` - all other subnet
* properties are ignored.
*
* @default false
*/
readonly reserved?: boolean;
}

/**
Expand Down Expand Up @@ -501,6 +513,12 @@ export class VpcNetwork extends VpcNetworkBase {

private createSubnetResources(subnetConfig: SubnetConfiguration, cidrMask: number) {
this.availabilityZones.forEach((zone, index) => {
if (subnetConfig.reserved === true) {
// For reserved subnets, just allocate ip space but do not create any resources
this.networkBuilder.addSubnet(cidrMask);
return;
}

const name = subnetId(subnetConfig.name, index);
const subnetProps: VpcSubnetProps = {
availabilityZone: zone,
Expand Down
68 changes: 68 additions & 0 deletions packages/@aws-cdk/aws-ec2/test/test.vpc.ts
Original file line number Diff line number Diff line change
Expand Up @@ -115,6 +115,74 @@ export = {
test.done();
},

"with subnets and reserved subnets defined, VPC subnet count should not contain reserved subnets "(test: Test) {
const stack = getTestStack();
new VpcNetwork(stack, 'TheVPC', {
cidr: '10.0.0.0/16',
subnetConfiguration: [
{
cidrMask: 24,
subnetType: SubnetType.Private,
name: 'Private',
},
{
cidrMask: 24,
name: 'reserved',
subnetType: SubnetType.Private,
reserved: true,
},
{
cidrMask: 28,
name: 'rds',
subnetType: SubnetType.Isolated,
}
],
maxAZs: 3
});
expect(stack).to(countResources("AWS::EC2::Subnet", 6));
test.done();
},
"with reserved subents, any other subnets should not have cidrBlock from within reserved space"(test: Test) {
const stack = getTestStack();
new VpcNetwork(stack, 'TheVPC', {
cidr: '10.0.0.0/16',
subnetConfiguration: [
{
cidrMask: 24,
name: 'ingress',
subnetType: SubnetType.Private,
},
{
cidrMask: 24,
name: 'reserved',
subnetType: SubnetType.Private,
reserved: true,
},
{
cidrMask: 24,
name: 'rds',
subnetType: SubnetType.Private,
}
],
maxAZs: 3
});
for (let i = 0; i < 3; i++) {
expect(stack).to(haveResource("AWS::EC2::Subnet", {
CidrBlock: `10.0.${i}.0/24`
}));
}
for (let i = 3; i < 6; i++) {
expect(stack).notTo(haveResource("AWS::EC2::Subnet", {
CidrBlock: `10.0.${i}.0/24`
}));
}
for (let i = 6; i < 9; i++) {
expect(stack).to(haveResource("AWS::EC2::Subnet", {
CidrBlock: `10.0.${i}.0/24`
}));
}
test.done();
},
"with custom subents, the VPC should have the right number of subnets, an IGW, and a NAT Gateway per AZ"(test: Test) {
const stack = getTestStack();
const zones = new AvailabilityZoneProvider(stack).availabilityZones.length;
Expand Down

0 comments on commit 4819ff4

Please sign in to comment.