Skip to content

Commit 5d501f4

Browse files
authored
Fix for Circular dependency while creating RouteTable and a VPCEndpoint (#142)
Issue: aws-controllers-k8s/community#1817 Description of changes: This is for fixing circular dependency bug while creating a new RouteTable and a VPCEndpoint. As per the changes in this PR, any route to `DestinationPrefixList` which is owned by "AWS" will be ignored while syncing routes and will be used as is. Tested via following : ``` apiVersion: ec2.services.k8s.aws/v1alpha1 kind: VPC metadata: name: circular-dependency-vpc spec: cidrBlocks: - 10.10.0.0/16 enableDNSSupport: True enableDNSHostnames: True tags: - key: Name value: circular-dependency-vpc --- apiVersion: ec2.services.k8s.aws/v1alpha1 kind: RouteTable metadata: name: circular-dependency-rtb spec: routes: - destinationCIDRBlock: 0.0.0.0/0 gatewayRef: from: name: circular-dependency-igw vpcRef: from: name: circular-dependency-vpc --- apiVersion: ec2.services.k8s.aws/v1alpha1 kind: VPCEndpoint metadata: name: circular-dependency-vpc-endpoint-s3 spec: serviceName: com.amazonaws.us-west-2.s3 vpcRef: from: name: circular-dependency-vpc routeTableRefs: - from: name: circular-dependency-rtb tags: - key: Name value: circular-dependency-vpc-endpoint-s3 --- apiVersion: ec2.services.k8s.aws/v1alpha1 kind: InternetGateway metadata: name: circular-dependency-igw spec: vpcRef: from: name: circular-dependency-vpc tags: - key: "Adobe.Owner" value: "Ethos" - key: Name value: circular-dependency-igw ``` Status (before reconciliation i.e. before ACK synced from AWS actual resources): ``` $ kubectl get routetable circular-dependency-rtb -o json | jq .status.routeStatuses [ { "destinationCIDRBlock": "10.10.0.0/16", "gatewayID": "local", "origin": "CreateRouteTable", "state": "active" }, { "destinationCIDRBlock": "0.0.0.0/0", "gatewayID": "igw-06d5825a3e9ba9dee", "origin": "CreateRoute", "state": "active" } ] ``` Status (after reconciliation i.e. after ACK synced from AWS actual resources): ``` [ { "destinationCIDRBlock": "10.10.0.0/16", "gatewayID": "local", "origin": "CreateRouteTable", "state": "active" }, { "destinationCIDRBlock": "0.0.0.0/0", "gatewayID": "igw-06d5825a3e9ba9dee", "origin": "CreateRoute", "state": "active" }, { "destinationPrefixListID": "pl-68a54001", "gatewayID": "vpce-017f902c0d2c007e3", "origin": "CreateRoute", "state": "active" } ] ``` By submitting this pull request, I confirm that my contribution is made under the terms of the Apache 2.0 license.
1 parent 5f6b119 commit 5d501f4

File tree

1 file changed

+56
-0
lines changed

1 file changed

+56
-0
lines changed

pkg/resource/route_table/hooks.go

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,11 +15,13 @@ package route_table
1515

1616
import (
1717
"context"
18+
"strings"
1819

1920
svcapitypes "github.com/aws-controllers-k8s/ec2-controller/apis/v1alpha1"
2021
ackcompare "github.com/aws-controllers-k8s/runtime/pkg/compare"
2122
ackrtlog "github.com/aws-controllers-k8s/runtime/pkg/runtime/log"
2223
svcsdk "github.com/aws/aws-sdk-go/service/ec2"
24+
"github.com/samber/lo"
2325
)
2426

2527
const LocalRouteGateway = "local"
@@ -45,6 +47,13 @@ func (rm *resourceManager) syncRoutes(
4547
toAdd := []*svcapitypes.CreateRouteInput{}
4648
toDelete := []*svcapitypes.CreateRouteInput{}
4749

50+
if latest != nil {
51+
latest.ko.Spec.Routes = rm.excludeAwsRoute(latest.ko.Spec.Routes)
52+
}
53+
if desired != nil {
54+
desired.ko.Spec.Routes = rm.excludeAwsRoute(desired.ko.Spec.Routes)
55+
}
56+
4857
for _, desiredRoute := range desired.ko.Spec.Routes {
4958
if (*desiredRoute).GatewayID != nil && *desiredRoute.GatewayID == LocalRouteGateway {
5059
// no-op for default route
@@ -290,6 +299,53 @@ func removeLocalRoute(
290299
return ret
291300
}
292301

302+
func (rm *resourceManager) excludeAwsRoute(
303+
routes []*svcapitypes.CreateRouteInput,
304+
) (ret []*svcapitypes.CreateRouteInput) {
305+
ret = make([]*svcapitypes.CreateRouteInput, 0)
306+
var prefixListIds []*string
307+
308+
// Preparing a list of prefixIds from all the DestinationPrefixListIDs in Routes
309+
// This is to prevent multiple AWS API calls of DescribeManagedPrefixLists
310+
311+
ret = lo.Reject(routes, func(route *svcapitypes.CreateRouteInput, index int) bool {
312+
return route.DestinationPrefixListID != nil
313+
})
314+
315+
prefix_list_routes := lo.Filter(routes, func(route *svcapitypes.CreateRouteInput, index int) bool {
316+
return route.DestinationPrefixListID != nil
317+
})
318+
319+
prefixListIds = lo.Map(prefix_list_routes, func(route *svcapitypes.CreateRouteInput, _ int) *string {
320+
return route.DestinationPrefixListID
321+
322+
})
323+
324+
var resp *svcsdk.DescribeManagedPrefixListsOutput
325+
input := &svcsdk.DescribeManagedPrefixListsInput{}
326+
input.PrefixListIds = prefixListIds
327+
resp, _ = rm.sdkapi.DescribeManagedPrefixLists(input)
328+
rm.metrics.RecordAPICall("READ_MANY", "DescribeManagedPrefixLists", nil)
329+
330+
m := lo.FilterMap(resp.PrefixLists, func(mpl *svcsdk.ManagedPrefixList, _ int) (string, bool) {
331+
if strings.EqualFold(*mpl.OwnerId, "AWS") {
332+
return *mpl.PrefixListId, true
333+
}
334+
return "", false
335+
})
336+
337+
filtered_routes := lo.FilterMap(prefix_list_routes, func(route *svcapitypes.CreateRouteInput, _ int) (*svcapitypes.CreateRouteInput, bool) {
338+
found := lo.IndexOf(m, *route.DestinationPrefixListID)
339+
if found == -1 {
340+
return route, true
341+
}
342+
return nil, false
343+
})
344+
ret = append(ret, filtered_routes...)
345+
346+
return ret
347+
}
348+
293349
// syncTags used to keep tags in sync by calling Create and Delete Tags API's
294350
func (rm *resourceManager) syncTags(
295351
ctx context.Context,

0 commit comments

Comments
 (0)