-
Notifications
You must be signed in to change notification settings - Fork 4k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
(BREAKING) Enhancement - Custom VPC Subnets #250
Changes from 7 commits
28aea13
2beb4a7
2a172ed
9da7a2f
55e3666
a2b527d
c9d4f39
af75c41
88a6d94
9ac38f4
60ef32d
0f0079a
abd5086
6dabebe
7be9cc5
2d1bb07
1ef7db6
06350b4
455d9ef
701858c
5ecb64a
a5ce5d9
2fbb213
1777874
9d8067f
d639de4
f2d4571
5c1bc5d
0864bb8
776f1e0
82246ae
864a341
9722cdc
bba5e67
c2baa77
31057b2
f3f5428
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -86,4 +86,154 @@ export class NetworkUtils { | |
|
||
} | ||
|
||
public static validIp(ipAddress: string): boolean { | ||
return ipAddress.split('.').map((octet: string) => parseInt(octet, 10)). | ||
filter((octet: number) => octet >= 0 && octet <= 255).length === 4; | ||
} | ||
/** | ||
* | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Please put whitespace between functions, start docstrings with a short caption string (start with a capital), and don't put whitespace between the docstring and the function itself. So this:
And not this:
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. fixed |
||
* takes an IP Address (e.g. 174.66.173.168) and converts to a number | ||
* (e.g 2923605416); currently only supports IPv4 | ||
* | ||
* Uses the formula: | ||
* (first octet * 256³) + (second octet * 256²) + (third octet * 256) + | ||
* (fourth octet) | ||
* | ||
* @param {string} the IP address (e.g. 174.66.173.168) | ||
* @returns {number} the integer value of the IP address (e.g 2923605416) | ||
*/ | ||
|
||
public static ipToNum(ipAddress: string): number { | ||
if (!this.validIp(ipAddress)) { | ||
throw new Error(`${ipAddress} is not valid`); | ||
} | ||
|
||
return ipAddress | ||
.split('.') | ||
.reduce( | ||
(p: number, c: string, i: number) => p + parseInt(c, 10) * 256 ** (3 - i), | ||
0 | ||
); | ||
} | ||
|
||
/** | ||
* | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Same remark about doc comments. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. fixed |
||
* Takes a number (e.g 2923605416) and converts it to an IPv4 address string | ||
* currently only supports IPv4 | ||
* | ||
* @param {number} the integer value of the IP address (e.g 2923605416) | ||
* @returns {string} the IPv4 address (e.g. 174.66.173.168) | ||
*/ | ||
|
||
public static numToIp(ipNum: number): string { | ||
// this all because bitwise math is signed | ||
let remaining = ipNum; | ||
const address = []; | ||
for (let i = 0; i < 4; i++) { | ||
if (remaining !== 0) { | ||
address.push(Math.floor(remaining / 256 ** (3 - i))); | ||
remaining = remaining % 256 ** (3 - i); | ||
} else { | ||
address.push(0); | ||
} | ||
} | ||
const ipAddress: string = address.join('.'); | ||
if ( !this.validIp(ipAddress) ) { | ||
throw new Error(`${ipAddress} is not a valid IP Address`); | ||
} | ||
return ipAddress; | ||
} | ||
|
||
} | ||
|
||
export class NetworkBuilder { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This should have a documentation comment describing the functionality, and a usage example. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. yes anything exported will get full docs. Hopefully I get a complete draft today/tonight There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Awesome. Thanks for contributing too! |
||
public readonly networkCidr: CidrBlock; | ||
protected subnetCidrs: CidrBlock[]; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Is this class being inherited from? If not, (And even if so, take a long hard look and see if Can also be initialized here to an empty array:
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. fixed used There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. also converted all protected -- I thought I would extend this for NetworkBuilder but chose to separate it. |
||
protected maxIpConsumed: string; | ||
|
||
constructor(cidr: string) { | ||
this.networkCidr = new CidrBlock(cidr); | ||
this.subnetCidrs = []; | ||
this.maxIpConsumed = NetworkUtils.numToIp(NetworkUtils. | ||
ipToNum(this.networkCidr.minIp()) - 1); | ||
} | ||
|
||
public addSubnet(mask: number): string { | ||
return this.addSubnets(mask, 1)[0]; | ||
} | ||
|
||
public addSubnets(mask: number, count?: number): string[] { | ||
if (mask < 16 || mask > 28) { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I am being insanely pedantic - but the I don't think it's something we need to change right now - but just something we need to think about how we want to handle in the future (more services have limits like this, where like 95% of people will hit the default limits, but not everyone) There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. agree - I was hoping that was out of scope for this first round since /16 was already in here before me. This brings up a question I was wondering about: https://docs.aws.amazon.com/AmazonVPC/latest/UserGuide/VPC_Subnets.html#VPC_Sizing. The recommended is use Private space and play by the rules, but technically I could deploy things that are not very useful. Where should we draw the line between good practice and rule here? Not sure this PR is the best place for a general discussion, but I'm definitely not sure how to answer this question tonight. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Should we also not enforce that the requested blocks can fit in the remaining address space beforehand? Seems simpler than calling Can we not do something like
|
||
throw new InvalidCidrRangeError(`x.x.x.x/${mask}`); | ||
} | ||
const subnets: CidrBlock[] = []; | ||
if (!count) { | ||
count = 1; | ||
} | ||
for (let i = 0; i < count; i ++) { | ||
const subnet: CidrBlock = CidrBlock.fromOffsetIp(this.maxIpConsumed, mask); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It seems more logical to me to instead of using It gets initialized to There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. done, went with |
||
this.maxIpConsumed = subnet.maxIp(); | ||
this.subnetCidrs.push(subnet); | ||
if (!this.validate()) { | ||
throw new Error(`${this.networkCidr.cidr} does not fully contain ${subnet.cidr}`); | ||
} | ||
subnets.push(subnet); | ||
} | ||
return subnets.map((subnet) => (subnet.cidr)); | ||
} | ||
|
||
public get subnets(): string[] { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I wonder if (I'm not a stickler for names - so I'll defer to how you feel about it) There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
I don't really have a good name for this, but I feel there should be a word that describes the "string representation of a CIDR block" in "x.x.x.x/y" form, which is distinct from CIDR (which just describes the block, afai understand), and subnet (which COULD be this but in the context of AWS I would expect it to represent a Subnet object) There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. done |
||
return this.subnetCidrs.map((subnet) => (subnet.cidr)); | ||
} | ||
|
||
public validate(): boolean { | ||
return this.subnetCidrs.map( | ||
(cidrBlock) => this.networkCidr.containsCidr(cidrBlock)). | ||
filter( (contains) => (contains === false)).length === 0; | ||
} | ||
|
||
} | ||
|
||
export class CidrBlock { | ||
|
||
public static calculateNetmask(mask: number): string { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Nit: Is There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. good question. I made this public because of the |
||
return NetworkUtils.numToIp(2 ** 32 - 2 ** (32 - mask)); | ||
} | ||
|
||
public static fromOffsetIp(ipAddress: string, mask: number): CidrBlock { | ||
const initialCidr = new CidrBlock(`${ipAddress}/${mask}`); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Feels like this should just be a method on Given that you already have a block, you want to answer the question "what's the next block?"
Which would behave similarly to Simialrly, I have a feeling that if we were to parse the
Then a lot of the operations on this become a lot easier.
Right? There's a lot of numToIp() and and IpToNum() happening that really only needs to happen at the edges, where the system interacts with humans. Internally everything can be binary numbers. It should be possible to overload the constructor in TypeScript, so that we can pass either There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. ok -- I think this is done. I'm not sure Typescript and I agree on constructor overloading design, but perhaps there is a simpler way and I didn't know it? |
||
const baseIpNum = NetworkUtils.ipToNum(initialCidr.maxIp()) + 1; | ||
return new this(`${NetworkUtils.numToIp(baseIpNum)}/${mask}`); | ||
} | ||
|
||
public readonly cidr: string; | ||
public readonly netmask: string; | ||
public readonly mask: number; | ||
|
||
constructor(cidr: string) { | ||
this.cidr = cidr; | ||
this.mask = parseInt(cidr.split('/')[1], 10); | ||
this.netmask = CidrBlock.calculateNetmask(this.mask); | ||
} | ||
|
||
public maxIp(): string { | ||
const minIpNum = NetworkUtils.ipToNum(this.minIp()); | ||
return NetworkUtils.numToIp(minIpNum + 2 ** (32 - this.mask) - 1); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think just a comment explaining this line will help future CDKers 😃 |
||
} | ||
|
||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Eventually make sure to add jsdocs to all exported entities |
||
public minIp(): string { | ||
const netmaskOct = this.netmask.split('.'); | ||
const ipOct = this.cidr.split('/')[0].split('.'); | ||
// tslint:disable:no-bitwise | ||
return netmaskOct.map( | ||
(maskOct, index) => parseInt(maskOct, 10) & parseInt(ipOct[index], 10)).join('.'); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Likewise. |
||
// tslint:enable:no-bitwise | ||
} | ||
|
||
public containsCidr(cidr: CidrBlock): boolean { | ||
return [ | ||
NetworkUtils.ipToNum(this.minIp()) <= NetworkUtils.ipToNum(cidr.minIp()), | ||
NetworkUtils.ipToNum(this.maxIp()) >= NetworkUtils.ipToNum(cidr.maxIp()), | ||
].filter((contains) => (contains === false)).length === 0; | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,6 +1,7 @@ | ||
import { AvailabilityZoneProvider, Construct, Tag, Token } from '@aws-cdk/core'; | ||
import { ec2 } from '@aws-cdk/resources'; | ||
import { NetworkUtils } from './network-util'; | ||
import { Obj } from '@aws-cdk/util'; | ||
import { NetworkBuilder, NetworkUtils } from './network-util'; | ||
import { VpcNetworkId, VpcNetworkRef, VpcSubnetId, VpcSubnetRef } from './vpc-ref'; | ||
/** | ||
* VpcNetworkProps allows you to specify configuration options for a VPC | ||
|
@@ -58,6 +59,33 @@ export interface VpcNetworkProps { | |
* @default All AZs in the region | ||
*/ | ||
maxAZs?: number; | ||
|
||
/** | ||
* Configure the subnets to build for each AZ | ||
* | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Use "@default" to describe the default behavior There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Also, what is the default VPC configuration? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. currently I left the default as is. Likely I'll keep this end state the same, but replace reduce the split cidr logic. |
||
* The subnets are constructed in the context of the VPC so you only need | ||
* specify the configuration. The VPC details (VPC ID, specific CIDR, | ||
* specific AZ will be calculated during creation) | ||
* | ||
* For example if you want three private subnets and three public subnets | ||
* across 3 AZs then maxAZs = 3 and provide the following: | ||
* subnets: [ | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Fix indentation here. Strings need quotes. Some note about optionality of |
||
* { | ||
* cidrMask: 24, | ||
* name: ingress, | ||
* subnetType: SubnetType.Public, | ||
* natGateway: true, | ||
* }, | ||
* { | ||
* cidrMask: 24, | ||
* name: application, | ||
* subnetType: SubnetType.Private, | ||
* } | ||
* ] | ||
* @default the VPC CIDR will be evenly divided between 1 public and 1 | ||
* private subnet per AZ | ||
*/ | ||
subnetConfigurations?: SubnetConfiguration[]; | ||
} | ||
|
||
/** | ||
|
@@ -142,6 +170,11 @@ export class VpcNetwork extends VpcNetworkRef { | |
*/ | ||
public readonly privateSubnets: VpcSubnetRef[] = []; | ||
|
||
/** | ||
* List of internal subnets in this VPC | ||
*/ | ||
public readonly internalSubnets: VpcSubnetRef[] = []; | ||
|
||
/** | ||
* The VPC resource | ||
*/ | ||
|
@@ -185,7 +218,7 @@ export class VpcNetwork extends VpcNetworkRef { | |
outboundTraffic === OutboundTrafficMode.FromPublicAndPrivateSubnets; | ||
|
||
// Create public and private subnets in each AZ | ||
this.createSubnets(cidrBlock, outboundTraffic, props.maxAZs); | ||
this.createSubnets(cidrBlock, outboundTraffic, props.maxAZs, props.subnetConfigurations); | ||
|
||
// Create an Internet Gateway and attach it (if the outbound traffic mode != None) | ||
if (allowOutbound) { | ||
|
@@ -213,7 +246,11 @@ export class VpcNetwork extends VpcNetworkRef { | |
* createSubnets takes a VPC, and creates a public and private subnet | ||
* in each Availability Zone. | ||
*/ | ||
private createSubnets(cidr: string, outboundTraffic: OutboundTrafficMode, maxAZs?: number) { | ||
private createSubnets( | ||
cidr: string, | ||
outboundTraffic: OutboundTrafficMode, | ||
maxAZs?: number, | ||
subnets?: SubnetConfiguration[]) { | ||
|
||
// Calculate number of public/private subnets based on number of AZs | ||
const zones = new AvailabilityZoneProvider(this).availabilityZones; | ||
|
@@ -224,11 +261,16 @@ export class VpcNetwork extends VpcNetworkRef { | |
zones.splice(maxAZs); | ||
} | ||
|
||
// Split the CIDR range into each availablity zone | ||
const ranges = NetworkUtils.splitCIDR(cidr, zones.length); | ||
if (subnets != null) { | ||
const networkBuilder = new NetworkBuilder(cidr); | ||
this.createCustomSubnets(networkBuilder, subnets, zones); | ||
} else { | ||
// Split the CIDR range into each availablity zone | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Why can't we use NetworkBuilder for this use case as well? If I understand correctly, should be possible... Less branching There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I was just going for minimal change, but I can easily do this. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I would like to see that if When you do so, please run the integ tests to verify that the generated template is still the same. |
||
const ranges = NetworkUtils.splitCIDR(cidr, zones.length); | ||
|
||
for (let i = 0; i < zones.length; i++) { | ||
for (let i = 0; i < zones.length; i++) { | ||
this.createSubnetPair(ranges[i], zones[i], i + 1, outboundTraffic); | ||
} | ||
} | ||
|
||
} | ||
|
@@ -263,9 +305,114 @@ export class VpcNetwork extends VpcNetworkRef { | |
this.privateSubnets.push(privateSubnet); | ||
this.dependencyElements.push(publicSubnet, privateSubnet); | ||
} | ||
private createCustomSubnets(builder: NetworkBuilder, subnets: SubnetConfiguration[], zones: string[]) { | ||
|
||
const natGatewayByAZ: Obj<Token> = {}; | ||
|
||
for (const subnet of subnets) { | ||
let azs = zones; | ||
if (subnet.numAZs != null) { | ||
azs = zones.slice(subnet.numAZs); | ||
} | ||
for (const zone of azs) { | ||
const cidr: string = builder.addSubnet(subnet.cidrMask); | ||
const name: string = `${subnet.name}AZ${zone.substr(-1)}`; | ||
switch (subnet.subnetType) { | ||
case SubnetType.Public: | ||
const publicSubnet = new VpcPublicSubnet(this, name, { | ||
mapPublicIpOnLaunch: subnet.mapPublicIpOnLaunch || true, | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I'm really bad at type script - but does There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Unrelated : I'm (mildy) curious why There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. My only legit use case is a bastion host. The public IPs that get put onto these images cannot be found in any CloudTrail. In some sensitive environments the non-traceable IP that is mapped causes audit trail complexities. Beyond that use case it is very odd to not want a public IP on a host in public subnet, but I suspect there are some obscure use cases beyond mine. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
You're very right, this is a bug! It indeed means always true! The correct way to write it would have been:
|
||
vpcId: this.vpcId, | ||
availabilityZone: zone, | ||
cidrBlock: cidr | ||
}); | ||
if (subnet.natGateway) { | ||
natGatewayByAZ[zone] = publicSubnet.addNatGateway(); | ||
} | ||
this.publicSubnets.push(publicSubnet); | ||
break; | ||
case SubnetType.Private: | ||
const privateSubnet = new VpcPrivateSubnet(this, name, { | ||
mapPublicIpOnLaunch: subnet.mapPublicIpOnLaunch || false, | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Interesting - what's the case for a subnet having this set to true? Is there ever a case where we'd do this? (I could totally be wrong - it's been a day and my brain is 💀 ) There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This one I have no use case for and I've never seen it done. However, I'm not a networking expert. |
||
vpcId: this.vpcId, | ||
availabilityZone: zone, | ||
cidrBlock: cidr | ||
}); | ||
this.privateSubnets.push(privateSubnet); | ||
break; | ||
case SubnetType.Internal: | ||
const internalSubnet = new VpcPrivateSubnet(this, name, { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
|
||
mapPublicIpOnLaunch: subnet.mapPublicIpOnLaunch || false, | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. In reading this - I wonder do we need the Why would we do a Just seems like we could drop the field maybe? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I'm guessing behind the scenes there may be some code that was just easier if the data structures were the same. However, I'll leave this up to the team to decide (not sure if that means @eladb or a polly vote in slack -- I'm pretty new here). There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Definitely it's easier if the internal data structures are the same, but we could and should still catch nonsensical configuration at the edges. So the only use case we've seen so far is setting this value to
But if you're tight on address space you could also just make the subnets smaller... so doesn't it make more sense to always leave this to the behavior that's natural for the subnet type? |
||
vpcId: this.vpcId, | ||
availabilityZone: zone, | ||
cidrBlock: cidr | ||
}); | ||
this.internalSubnets.push(internalSubnet); | ||
break; | ||
} | ||
} | ||
} | ||
(this.privateSubnets as VpcPrivateSubnet[]).forEach((privateSubnet, i) => { | ||
let ngwId = natGatewayByAZ[privateSubnet.availabilityZone]; | ||
if (ngwId === undefined) { | ||
const ngwArray = Array.from(Object.values(natGatewayByAZ)); | ||
ngwId = ngwArray[i % ngwArray.length]; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. A helpful comment line would be AMAZING 😺 |
||
} | ||
privateSubnet.addDefaultNatRouteEntry(ngwId); | ||
}); | ||
} | ||
|
||
} | ||
|
||
/** | ||
* The type of Subnet | ||
*/ | ||
export enum SubnetType { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I like the concept of defining the network access (and whether NAT gateways etc are required) at a subnet level rather than a VPC level. I do feel that as it is, it's slightly confusing to be providing this at a VpcNetwork level (with I realise you've done it this way to minimise breaking changes on the VpcNetwork construct, however it would be really nice if we:
I think this would be a more coherent API for customers. @rix0rrr what do you think about this (would be a breaking change). From what I can see no other part of the CDK uses this There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think this would simplify the construct implementation too - You could just define a There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Breaking changes are fine at this stage of the project. We want the best APIs for 1.0.0. Just make sure to document in the commit/PR message under "BREAKING CHANGE" (see CONTRIBUTING guide). There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I like the proposal for the breaking change. I honestly struggled with this as well. I also flip flopped on the implementation between which one should win; right now it's actually not well enforced and I agree confusing. I'll wait for more consensus before making a change. Didn't see @eladb comment. I'll work up the change. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Something else I would like to bring up: Does the terminology still make sense?We now have PUBLIC (clear enough), and both PRIVATE and INTERNAL. I'm fine if these terms are commonly used and well-understood, but the distinction between private and internal isn't obvious to me from the term. I guess private is standardized and well-understood, so that means we should find a more descriptive term for internal.
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. isolated There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. YES! |
||
|
||
/** | ||
* Internal Subnets do not route Outbound traffic | ||
* | ||
* This can be good for subnets with RDS or | ||
* Elasticache endpoints | ||
*/ | ||
Internal = 1, | ||
|
||
/** | ||
* Public subnets route outbound traffic via an Internet Gateway | ||
* | ||
* If this is set and OutboundTrafficMode.None is configure an error | ||
* will be thrown. | ||
*/ | ||
Public = 2, | ||
|
||
/** | ||
* Private subnets route outbound traffic via a NAT Gateway | ||
* | ||
* Outbound traffic will be routed via a NAT Gateways preference being in | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Split doc text into caption line/body. |
||
* the same AZ, but if not available will use another AZ. This is common for | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The remark about "cost conscious", is that about private subnets in general or just the case where there might be fewer NAT gateways than subnets? Is that also the purpose of There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yes. We create 90 day use accounts for experimentation. In these accounts we commonly use only one NAT GW. I need to test this feature, but you are inferring the right intent. Should I change wording to be more clear? The purpose of numAZs is the rare case when you don't want the subnet in all AZs. This is where I get a little skeptical but the use case is commonly data replication over a VPN where the IP space is constrained. I don't see this very often, but I thought we could easily support. If we think this is going to add too much complexity you can probably talk me out of this. |
||
* experimental cost conscious accounts or accounts where HA outbound | ||
* traffic is not needed. | ||
*/ | ||
Private = 3 | ||
} | ||
|
||
/** | ||
* Specify configuration parameters for a VPC to be built | ||
*/ | ||
export interface SubnetConfiguration { | ||
// the cidr mask value from 16-28 | ||
cidrMask: number; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Could we make this optional? For example, by splitting unclaimed address space evenly over the subnets? I'm thinking of the following calculation:
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Let me give this a try. I can't think of a reason it will not work, but hard to play out all the situations. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. this is now included |
||
// Public (IGW), Private (Nat GW), Internal (no outbound) | ||
subnetType: SubnetType; | ||
// name that will be used to generate an AZ specific name e.g. name-2a | ||
name: string; | ||
// if true will place a NAT Gateway in this subnet, subnetType must be Public | ||
natGateway?: boolean; | ||
// defaults to true in Subnet.Public, false in Subnet.Private or Subnet.Internal | ||
mapPublicIpOnLaunch?: boolean; | ||
// number of AZs to build this subnet in | ||
numAZs?: number; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I'm very curious about the interaction between There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. see comment below regarding constrained IP space environments. I might be missing some checks to ensure There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I now make this check and error if applicable |
||
} | ||
|
||
/** | ||
* Specify configuration parameters for a VPC subnet | ||
*/ | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Don't think this function is correct.
Won't it return
true
for the following:?
Needs a docstring too.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
done - added tests to verify