Skip to content
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

Handle aws implicit and shared routing tables #22019

Merged
merged 1 commit into from
Feb 27, 2016
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
71 changes: 48 additions & 23 deletions pkg/cloudprovider/providers/aws/aws.go
Original file line number Diff line number Diff line change
Expand Up @@ -1852,11 +1852,10 @@ func (s *AWSCloud) createTags(resourceID string, tags map[string]string) error {
}

func (s *AWSCloud) listPublicSubnetIDsinVPC(vpcId string) ([]string, error) {
subnetIds := []string{}

sRequest := &ec2.DescribeSubnetsInput{}
filters := []*ec2.Filter{}
filters = append(filters, newEc2Filter("vpc-id", vpcId))
vpcIdFilter := newEc2Filter("vpc-id", vpcId)
var filters []*ec2.Filter
filters = append(filters, vpcIdFilter)
filters = s.addFilters(filters)
sRequest.Filters = filters

Expand All @@ -1867,58 +1866,84 @@ func (s *AWSCloud) listPublicSubnetIDsinVPC(vpcId string) ([]string, error) {
}

rRequest := &ec2.DescribeRouteTablesInput{}
rRequest.Filters = filters
rRequest.Filters = []*ec2.Filter{vpcIdFilter}

rt, err := s.ec2.DescribeRouteTables(rRequest)
if err != nil {
glog.Error("error describing route tables: ", err)
return nil, err
}

var subnetIds []string
availabilityZones := sets.NewString()
for _, subnet := range subnets {
az := orEmpty(subnet.AvailabilityZone)
id := orEmpty(subnet.SubnetId)
if !isSubnetPublic(rt, id) {

isPublic, err := isSubnetPublic(rt, id)
if err != nil {
return nil, err
}
if !isPublic {
glog.V(2).Infof("Ignoring private subnet %q", id)
continue
}

if availabilityZones.Has(az) {
glog.Warning("Found multiple subnets per AZ '", az, "', ignoring subnet '", id, "'")
continue
}

subnetIds = append(subnetIds, id)
availabilityZones.Insert(az)
}

return subnetIds, nil
}

func isSubnetPublic(rt []*ec2.RouteTable, subnetID string) bool {
func isSubnetPublic(rt []*ec2.RouteTable, subnetID string) (bool, error) {
var subnetTable *ec2.RouteTable
for _, table := range rt {
var found bool
for _, assoc := range table.Associations {
if aws.StringValue(assoc.SubnetId) == subnetID {
found = true
subnetTable = table
break
}
}
if !found {
continue
}
for _, route := range table.Routes {
// There is no direct way in the AWS API to determine if a subnet is public or private.
// A public subnet is one which has an internet gateway route
// we look for the gatewayId and make sure it has the prefix of igw to differentiate
// from the default in-subnet route which is called "local"
// or other virtual gateway (starting with vgv)
// or vpc peering connections (starting with pcx).
if strings.HasPrefix(aws.StringValue(route.GatewayId), "igw") {
return true
}

if subnetTable == nil {
// If there is no explicit association, the subnet will be implicitly
// associated with the VPC's main routing table.
for _, table := range rt {
for _, assoc := range table.Associations {
if aws.BoolValue(assoc.Main) == true {
glog.V(4).Infof("Assuming implicit use of main routing table %s for %s",
aws.StringValue(table.RouteTableId), subnetID)
subnetTable = table
break
}
}
}
}
return false

if subnetTable == nil {
return false, fmt.Errorf("Could not locate routing table for subnet %s", subnetID)
}

for _, route := range subnetTable.Routes {
// There is no direct way in the AWS API to determine if a subnet is public or private.
// A public subnet is one which has an internet gateway route
// we look for the gatewayId and make sure it has the prefix of igw to differentiate
// from the default in-subnet route which is called "local"
// or other virtual gateway (starting with vgv)
// or vpc peering connections (starting with pcx).
if strings.HasPrefix(aws.StringValue(route.GatewayId), "igw") {
return true, nil
}
}

return false, nil
}

// EnsureLoadBalancer implements LoadBalancer.EnsureLoadBalancer
Expand Down Expand Up @@ -1963,7 +1988,7 @@ func (s *AWSCloud) EnsureLoadBalancer(name, region string, publicIP net.IP, port
// Construct list of configured subnets
subnetIDs, err := s.listPublicSubnetIDsinVPC(vpcId)
if err != nil {
glog.Error("Error listing subnets in VPC", err)
glog.Error("Error listing subnets in VPC: ", err)
return nil, err
}

Expand Down
2 changes: 1 addition & 1 deletion pkg/cloudprovider/providers/aws/aws_loadbalancer.go
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@ func (s *AWSCloud) ensureLoadBalancer(namespacedName types.NamespacedName, name
{Key: aws.String(TagNameKubernetesService), Value: aws.String(namespacedName.String())},
}

glog.Infof("Creating load balancer for %v with name: ", namespacedName, name)
glog.Infof("Creating load balancer for %v with name: %s", namespacedName, name)
_, err := s.elb.CreateLoadBalancer(createRequest)
if err != nil {
return nil, err
Expand Down
35 changes: 35 additions & 0 deletions pkg/cloudprovider/providers/aws/aws_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -769,6 +769,15 @@ func constructSubnet(id string, az string) *ec2.Subnet {
}

func constructRouteTables(routeTablesIn map[string]bool) (routeTablesOut []*ec2.RouteTable) {
routeTablesOut = append(routeTablesOut,
&ec2.RouteTable{
Associations: []*ec2.RouteTableAssociation{{Main: aws.Bool(true)}},
Routes: []*ec2.Route{{
DestinationCidrBlock: aws.String("0.0.0.0/0"),
GatewayId: aws.String("igw-main"),
}},
})

for subnetID := range routeTablesIn {
routeTablesOut = append(
routeTablesOut,
Expand Down Expand Up @@ -850,6 +859,32 @@ func TestSubnetIDsinVPC(t *testing.T) {
}
}

// test implicit routing table - when subnets are not explicitly linked to a table they should use main
awsServices.ec2.RouteTables = constructRouteTables(map[string]bool{})

result, err = c.listPublicSubnetIDsinVPC(vpcID)
if err != nil {
t.Errorf("Error listing subnets: %v", err)
return
}

if len(result) != 3 {
t.Errorf("Expected 3 subnets but got %d", len(result))
return
}

result_set = make(map[string]bool)
for _, v := range result {
result_set[v] = true
}

for i := range subnets {
if !result_set[subnets[i]["id"]] {
t.Errorf("Expected subnet%d '%s' in result: %v", i, subnets[i]["id"], result)
return
}
}

// test with 4 subnets from 3 different AZs
// add duplicate az subnet
subnets[3] = make(map[string]string)
Expand Down