Skip to content

Commit d9da7e9

Browse files
authored
Merge pull request #1 from rahulait/route-controller-multiple-vpcs
manage routes for instances in multiple vpcs in a single region
2 parents 1b046af + c04ffe9 commit d9da7e9

File tree

8 files changed

+160
-128
lines changed

8 files changed

+160
-128
lines changed

cloud/linode/cloud.go

Lines changed: 9 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -6,13 +6,13 @@ import (
66
"net"
77
"os"
88
"strconv"
9-
"sync"
109
"time"
1110

1211
"github.com/spf13/pflag"
1312
"golang.org/x/exp/slices"
1413
"k8s.io/client-go/informers"
1514
cloudprovider "k8s.io/cloud-provider"
15+
"k8s.io/klog/v2"
1616

1717
"github.com/linode/linode-cloud-controller-manager/cloud/linode/client"
1818
)
@@ -35,41 +35,14 @@ var Options struct {
3535
KubeconfigFlag *pflag.Flag
3636
LinodeGoDebug bool
3737
EnableRouteController bool
38+
// deprecated: use VPCNames instead
3839
VPCName string
40+
VPCNames string
3941
LoadBalancerType string
4042
BGPNodeSelector string
4143
LinodeExternalNetwork *net.IPNet
4244
}
4345

44-
// vpcDetails is set when VPCName options flag is set.
45-
// We use it to list instances running within the VPC if set
46-
type vpcDetails struct {
47-
mu sync.RWMutex
48-
id int
49-
name string
50-
}
51-
52-
func (v *vpcDetails) setDetails(client client.Client, name string) error {
53-
v.mu.Lock()
54-
defer v.mu.Unlock()
55-
56-
id, err := getVPCID(client, Options.VPCName)
57-
if err != nil {
58-
return fmt.Errorf("failed finding VPC ID: %w", err)
59-
}
60-
v.id = id
61-
v.name = name
62-
return nil
63-
}
64-
65-
func (v *vpcDetails) getID() int {
66-
v.mu.Lock()
67-
defer v.mu.Unlock()
68-
return v.id
69-
}
70-
71-
var vpcInfo vpcDetails = vpcDetails{id: 0, name: ""}
72-
7346
type linodeCloud struct {
7447
client client.Client
7548
instances cloudprovider.InstancesV2
@@ -114,11 +87,13 @@ func newCloud() (cloudprovider.Interface, error) {
11487
linodeClient.SetDebug(true)
11588
}
11689

90+
if Options.VPCName != "" && Options.VPCNames != "" {
91+
return nil, fmt.Errorf("cannot have both vpc-name and vpc-names set")
92+
}
93+
11794
if Options.VPCName != "" {
118-
err := vpcInfo.setDetails(linodeClient, Options.VPCName)
119-
if err != nil {
120-
return nil, fmt.Errorf("failed finding VPC ID: %w", err)
121-
}
95+
klog.Warningf("vpc-name flag is deprecated. Use vpc-names instead")
96+
Options.VPCNames = Options.VPCName
12297
}
12398

12499
routes, err := newRoutes(linodeClient)

cloud/linode/instances.go

Lines changed: 20 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -79,25 +79,36 @@ func (nc *nodeCache) refreshInstances(ctx context.Context, client client.Client)
7979

8080
// If running within VPC, find instances and store their ips
8181
vpcNodes := map[int][]string{}
82-
vpcID := vpcInfo.getID()
83-
if vpcID != 0 {
84-
resp, err := client.ListVPCIPAddresses(ctx, vpcID, linodego.NewListOptions(0, ""))
82+
vpcNames := strings.Split(Options.VPCNames, ",")
83+
for _, v := range vpcNames {
84+
vpcName := strings.TrimSpace(v)
85+
if vpcName == "" {
86+
continue
87+
}
88+
vpcID, err := GetVPCID(client, strings.TrimSpace(vpcName))
8589
if err != nil {
86-
return err
90+
klog.Errorf("failed updating instances cache for VPC %s. Error: %s", vpcName, err.Error())
91+
continue
8792
}
88-
for _, r := range resp {
89-
if r.Address == nil {
90-
continue
93+
if vpcID != 0 {
94+
resp, err := client.ListVPCIPAddresses(ctx, vpcID, linodego.NewListOptions(0, ""))
95+
if err != nil {
96+
return err
97+
}
98+
for _, r := range resp {
99+
if r.Address == nil {
100+
continue
101+
}
102+
vpcNodes[r.LinodeID] = append(vpcNodes[r.LinodeID], *r.Address)
91103
}
92-
vpcNodes[r.LinodeID] = append(vpcNodes[r.LinodeID], *r.Address)
93104
}
94105
}
95106

96107
newNodes := make(map[int]linodeInstance, len(instances))
97108
for i, instance := range instances {
98109

99110
// if running within VPC, only store instances in cache which are part of VPC
100-
if vpcID != 0 && len(vpcNodes[instance.ID]) == 0 {
111+
if Options.VPCNames != "" && len(vpcNodes[instance.ID]) == 0 {
101112
continue
102113
}
103114
node := linodeInstance{

cloud/linode/route_controller.go

Lines changed: 69 additions & 51 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import (
55
"fmt"
66
"os"
77
"strconv"
8+
"strings"
89
"sync"
910
"time"
1011

@@ -19,28 +20,40 @@ import (
1920
)
2021

2122
type routeCache struct {
22-
sync.RWMutex
23+
Mu sync.RWMutex
2324
routes map[int][]linodego.VPCIP
2425
lastUpdate time.Time
2526
ttl time.Duration
2627
}
2728

29+
// RefreshCache checks if cache has expired and updates it accordingly
2830
func (rc *routeCache) refreshRoutes(ctx context.Context, client client.Client) error {
29-
rc.Lock()
30-
defer rc.Unlock()
31+
rc.Mu.Lock()
32+
defer rc.Mu.Unlock()
3133

3234
if time.Since(rc.lastUpdate) < rc.ttl {
3335
return nil
3436
}
3537

3638
vpcNodes := map[int][]linodego.VPCIP{}
37-
vpcID := vpcInfo.getID()
38-
resp, err := client.ListVPCIPAddresses(ctx, vpcID, linodego.NewListOptions(0, ""))
39-
if err != nil {
40-
return err
41-
}
42-
for _, r := range resp {
43-
vpcNodes[r.LinodeID] = append(vpcNodes[r.LinodeID], r)
39+
vpcNames := strings.Split(Options.VPCNames, ",")
40+
for _, v := range vpcNames {
41+
vpcName := strings.TrimSpace(v)
42+
if vpcName == "" {
43+
continue
44+
}
45+
vpcID, err := GetVPCID(client, strings.TrimSpace(vpcName))
46+
if err != nil {
47+
klog.Errorf("failed updating cache for VPC %s. Error: %s", vpcName, err.Error())
48+
continue
49+
}
50+
resp, err := client.ListVPCIPAddresses(ctx, vpcID, linodego.NewListOptions(0, ""))
51+
if err != nil {
52+
return err
53+
}
54+
for _, r := range resp {
55+
vpcNodes[r.LinodeID] = append(vpcNodes[r.LinodeID], r)
56+
}
4457
}
4558

4659
rc.routes = vpcNodes
@@ -49,7 +62,6 @@ func (rc *routeCache) refreshRoutes(ctx context.Context, client client.Client) e
4962
}
5063

5164
type routes struct {
52-
vpcid int
5365
client client.Client
5466
instances *instances
5567
routeCache *routeCache
@@ -64,13 +76,11 @@ func newRoutes(client client.Client) (cloudprovider.Routes, error) {
6476
}
6577
klog.V(3).Infof("TTL for routeCache set to %d seconds", timeout)
6678

67-
vpcid := vpcInfo.getID()
68-
if Options.EnableRouteController && vpcid == 0 {
69-
return nil, fmt.Errorf("cannot enable route controller as vpc [%s] not found", Options.VPCName)
79+
if Options.EnableRouteController && Options.VPCNames == "" {
80+
return nil, fmt.Errorf("cannot enable route controller as vpc-names is empty")
7081
}
7182

7283
return &routes{
73-
vpcid: vpcid,
7484
client: client,
7585
instances: newInstances(client),
7686
routeCache: &routeCache{
@@ -82,8 +92,8 @@ func newRoutes(client client.Client) (cloudprovider.Routes, error) {
8292

8393
// instanceRoutesByID returns routes for given instance id
8494
func (r *routes) instanceRoutesByID(id int) ([]linodego.VPCIP, error) {
85-
r.routeCache.RLock()
86-
defer r.routeCache.RUnlock()
95+
r.routeCache.Mu.RLock()
96+
defer r.routeCache.Mu.RUnlock()
8797
instanceRoutes, ok := r.routeCache.routes[id]
8898
if !ok {
8999
return nil, fmt.Errorf("no routes found for instance %d", id)
@@ -135,22 +145,25 @@ func (r *routes) CreateRoute(ctx context.Context, clusterName string, nameHint s
135145
// check already configured routes
136146
intfRoutes := []string{}
137147
intfVPCIP := linodego.VPCIP{}
138-
for _, ir := range instanceRoutes {
139-
if ir.VPCID != r.vpcid {
140-
continue
141-
}
142148

143-
if ir.Address != nil {
144-
intfVPCIP = ir
145-
continue
146-
}
149+
for _, vpcid := range GetAllVPCIDs() {
150+
for _, ir := range instanceRoutes {
151+
if ir.VPCID != vpcid {
152+
continue
153+
}
147154

148-
if ir.AddressRange != nil && *ir.AddressRange == route.DestinationCIDR {
149-
klog.V(4).Infof("Route already exists for node %s", route.TargetNode)
150-
return nil
151-
}
155+
if ir.Address != nil {
156+
intfVPCIP = ir
157+
continue
158+
}
152159

153-
intfRoutes = append(intfRoutes, *ir.AddressRange)
160+
if ir.AddressRange != nil && *ir.AddressRange == route.DestinationCIDR {
161+
klog.V(4).Infof("Route already exists for node %s", route.TargetNode)
162+
return nil
163+
}
164+
165+
intfRoutes = append(intfRoutes, *ir.AddressRange)
166+
}
154167
}
155168

156169
if intfVPCIP.Address == nil {
@@ -185,21 +198,24 @@ func (r *routes) DeleteRoute(ctx context.Context, clusterName string, route *clo
185198
// check already configured routes
186199
intfRoutes := []string{}
187200
intfVPCIP := linodego.VPCIP{}
188-
for _, ir := range instanceRoutes {
189-
if ir.VPCID != r.vpcid {
190-
continue
191-
}
192201

193-
if ir.Address != nil {
194-
intfVPCIP = ir
195-
continue
196-
}
202+
for _, vpcid := range GetAllVPCIDs() {
203+
for _, ir := range instanceRoutes {
204+
if ir.VPCID != vpcid {
205+
continue
206+
}
197207

198-
if ir.AddressRange != nil && *ir.AddressRange == route.DestinationCIDR {
199-
continue
200-
}
208+
if ir.Address != nil {
209+
intfVPCIP = ir
210+
continue
211+
}
212+
213+
if ir.AddressRange != nil && *ir.AddressRange == route.DestinationCIDR {
214+
continue
215+
}
201216

202-
intfRoutes = append(intfRoutes, *ir.AddressRange)
217+
intfRoutes = append(intfRoutes, *ir.AddressRange)
218+
}
203219
}
204220

205221
if intfVPCIP.Address == nil {
@@ -234,17 +250,19 @@ func (r *routes) ListRoutes(ctx context.Context, clusterName string) ([]*cloudpr
234250
}
235251

236252
// check for configured routes
237-
for _, ir := range instanceRoutes {
238-
if ir.Address != nil || ir.VPCID != r.vpcid {
239-
continue
240-
}
253+
for _, vpcid := range GetAllVPCIDs() {
254+
for _, ir := range instanceRoutes {
255+
if ir.Address != nil || ir.VPCID != vpcid {
256+
continue
257+
}
241258

242-
if ir.AddressRange != nil {
243-
route := &cloudprovider.Route{
244-
TargetNode: types.NodeName(instance.Label),
245-
DestinationCIDR: *ir.AddressRange,
259+
if ir.AddressRange != nil {
260+
route := &cloudprovider.Route{
261+
TargetNode: types.NodeName(instance.Label),
262+
DestinationCIDR: *ir.AddressRange,
263+
}
264+
configuredRoutes = append(configuredRoutes, route)
246265
}
247-
configuredRoutes = append(configuredRoutes, route)
248266
}
249267
}
250268
}

0 commit comments

Comments
 (0)