fix: allow importing public subnets with no MapPublicIPOnLaunch#2457
fix: allow importing public subnets with no MapPublicIPOnLaunch#2457mergify[bot] merged 7 commits intoaws:mainlinefrom
Conversation
| type ListVPCSubnetsOpts func([]*ec2.Subnet) []*ec2.Subnet | ||
|
|
||
| // FilterForPublicSubnets is used to filter to get public subnets. | ||
| func FilterForPublicSubnets() ListVPCSubnetsOpts { |
There was a problem hiding this comment.
Should we not be figuring out if a subnet has an internet gateway? so that we filter out the ones that are not relevant
There was a problem hiding this comment.
It would be very difficult to tell since we need to check
- if the VPC has an internet gateway attached to it
- describe the route table check if it has route to the internet gateway and check if the route table is associated with the subnet (we would need to do it for every subnet within the VPC)
There was a problem hiding this comment.
Is the drawback that this is too fragile? If we can provide a good experience by making these API calls and if there are no valid subnets, prompt customers what changes they'd need to make to their subnet in order for it to be useful to Copilot?
There was a problem hiding this comment.
It's not too bad I think. We can describe the route tables for a given VPC from the user:
$ aws ec2 describe-route-tables --filters Name=vpc-id,Values=vpc-03e4614a3d0217256
In the response we'll look for SubnetId under Associations and for Routes to have a GatewayId that starts with igw-:
View JSON response
"Associations": [
{
"Main": false,
"RouteTableAssociationId": "rtbassoc-096f8559b5e31d9d3",
"RouteTableId": "rtb-073d682766628c37f",
"SubnetId": "subnet-018ccb78d353cec9b",
"AssociationState": {
"State": "associated"
}
},
{
"Main": false,
"RouteTableAssociationId": "rtbassoc-0ebf8e431225c1b20",
"RouteTableId": "rtb-073d682766628c37f",
"SubnetId": "subnet-005dc83a6473feee7",
"AssociationState": {
"State": "associated"
}
}
],
"PropagatingVgws": [],
"RouteTableId": "rtb-073d682766628c37f",
"Routes": [
{
"DestinationCidrBlock": "10.0.0.0/16",
"GatewayId": "local",
"Origin": "CreateRouteTable",
"State": "active"
},
{
"DestinationCidrBlock": "0.0.0.0/0",
"GatewayId": "igw-058421418aeacb2f0",
"Origin": "CreateRoute",
"State": "active"
}
]The only downside is we won't have tags associated with the subnets, but we can make two network calls to get that info.
| log.Errorf(`No existing private subnets were found in VPC %s. You can either: | ||
| - Create new private subnets and then import them. | ||
| log.Errorf(`No existing subnets were found in VPC %s. You can either: | ||
| - Create new subnets and then import them as private subnets. |
There was a problem hiding this comment.
I see it a bit confusing to say 'import them as private subnets', since whether they're private subnets doesn't depend on how they import them 🤔
83cdd88 to
27e70aa
Compare
| type ListVPCSubnetsOpts func([]*ec2.Subnet) []*ec2.Subnet | ||
|
|
||
| // FilterForPublicSubnets is used to filter to get public subnets. | ||
| func FilterForPublicSubnets() ListVPCSubnetsOpts { |
There was a problem hiding this comment.
Is the drawback that this is too fragile? If we can provide a good experience by making these API calls and if there are no valid subnets, prompt customers what changes they'd need to make to their subnet in order for it to be useful to Copilot?
27e70aa to
bd0c59e
Compare
| Private []VPCResource | ||
| } | ||
|
|
||
| // ListVPCSubnets lists all subnets given a VPC ID. Note that public subnets |
There was a problem hiding this comment.
| // ListVPCSubnets lists all subnets given a VPC ID. Note that public subnets | |
| // ListVPCSubnets lists all subnets with a given VPC ID. Note that public subnets |
| } | ||
|
|
||
| // ListVPCSubnets lists all subnets given a VPC ID. Note that public subnets | ||
| // are subnets associated with a route table that routed by an internet gateway. |
There was a problem hiding this comment.
the wording of my fix is kinda weird but maybe a little clearer?
| // are subnets associated with a route table that routed by an internet gateway. | |
| // are subnets associated with an internet gateway through a route table. |
|
|
||
| // ListVPCSubnets lists all subnets given a VPC ID. Note that public subnets | ||
| // are subnets associated with a route table that routed by an internet gateway. | ||
| // And the rest subnets are private subnets. |
There was a problem hiding this comment.
| // And the rest subnets are private subnets. | |
| // And the rest of the subnets are private. |
| log.Warningf(`No existing public subnets were found in VPC %s. | ||
| If you proceed without public subnets, you will not be able to deploy Load Balanced Web Services in this environment. | ||
| log.Warningf(`No existing subnets were found in VPC %s. | ||
| If you proceed without specifying public subnets, you will not be able to deploy Load Balanced Web Services in this environment. |
There was a problem hiding this comment.
Should we say "specifying two public subnets"?
There was a problem hiding this comment.
yeah maybe "without at least two public subnets"
huanjani
left a comment
There was a problem hiding this comment.
Looks great! Just some tiny wording nits. Feel free to remove DNM label when those are addressed (or ignored) 😄 .
| // VPCResource contains the ID and name of a VPCResource. | ||
| type VPCResource struct { |
There was a problem hiding this comment.
Why this rename? I liked reading ec2.VPC and ec2.Subnets better
There was a problem hiding this comment.
Oh I see you wanted to use ID and Name across resources, what do you thikn of this alternative:
type resource struct {
ID string
Name string
}
type VPC resource| // VPCSubnets are all subnets within a VPC. | ||
| type VPCSubnets struct { | ||
| Public []VPCResource | ||
| Private []VPCResource | ||
| } |
There was a problem hiding this comment.
🤔 this is confusing, why is a subnet composed of VPC resources?
What do you think of refactoring to:
type Subnet struct {
resource
Public bool
VPCID string
}
func (c *EC2) ListVPCSubnets(vpcID string) ([]Subnet, error) {There was a problem hiding this comment.
hmm following the logic here #2457 (comment) i think it might be clearer to just have
// VPCSubnets are all subnets within a VPC.
type VPCSubnets struct {
Public []Subnet
Private []Subnet
}| inputFilters := toEC2Filter(filters) | ||
| var routeTables []*ec2.RouteTable | ||
| input := &ec2.DescribeRouteTablesInput{ | ||
| Filters: inputFilters, | ||
| } |
There was a problem hiding this comment.
nit:
| inputFilters := toEC2Filter(filters) | |
| var routeTables []*ec2.RouteTable | |
| input := &ec2.DescribeRouteTablesInput{ | |
| Filters: inputFilters, | |
| } | |
| var routeTables []*ec2.RouteTable | |
| input := &ec2.DescribeRouteTablesInput{ | |
| Filters: toEC2Filter(filters), | |
| } |
| log.Warningf(`No existing public subnets were found in VPC %s. | ||
| If you proceed without public subnets, you will not be able to deploy Load Balanced Web Services in this environment. | ||
| log.Warningf(`No existing subnets were found in VPC %s. | ||
| If you proceed without specifying public subnets, you will not be able to deploy Load Balanced Web Services in this environment. |
There was a problem hiding this comment.
yeah maybe "without at least two public subnets"
| // PublicSubnets has the user multiselect public subnets given the VPC ID. | ||
| func (s *EC2Select) PublicSubnets(prompt, help, vpcID string) ([]string, error) { | ||
| return s.subnet(prompt, help, vpcID, ec2.FilterForPublicSubnets()) | ||
| return s.subnet(prompt, help, vpcID, true) |
There was a problem hiding this comment.
boolean params are usually a codesmell because it's not clear what the boolean stands for: https://martinfowler.com/bliki/FlagArgument.html
return s.selectPublicSubnets(prompt, help, vpcID)
return s.selectPrivateSubnets(prompt, help, vpcID)
func (s *EC2Select) selectPublicSubnets(prompt, help, vpcID string) ([]string, error) {
subnets, err := s.ec2.ListVPCSubnets(vpcID)
if err != nil { ... }
publicSubnets := .... // for-loop and filter on .Public
return s.selectSubnets(prompt, help, publicSubnets) // This method is common for s.selectPrivateSubnets
}d18b6d1 to
86be194
Compare
efekarakus
left a comment
There was a problem hiding this comment.
Feel free to merge after the nit!
| // VPC contains the ID and name of a VPC. | ||
| type VPC struct { | ||
| // Resource contains the ID and name of a EC2 resource. | ||
| type Resource struct { |
There was a problem hiding this comment.
does this need to be public?
You can still access the public fields even if the embedded struct is private so you should be able to do vpc.ID
There was a problem hiding this comment.
It's for unit test in the other pkg https://github.com/aws/copilot-cli/pull/2457/files#diff-c870c9dd1b1575fbdd8a11486b23829bc2d95e6121202ea397c5f14c528fa53dR48-R50
fixes #1724, fixes #2287
By submitting this pull request, I confirm that you can use, modify, copy, and redistribute this contribution, under the terms of your choice.