Skip to content

Commit 470f96e

Browse files
committed
wip: add support for using linode interfaces (beta-only)
1 parent cac095e commit 470f96e

File tree

11 files changed

+245
-30
lines changed

11 files changed

+245
-30
lines changed

cloud/linode/client/client.go

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,9 @@ type Client interface {
3535

3636
UpdateInstanceConfigInterface(context.Context, int, int, int, linodego.InstanceConfigInterfaceUpdateOptions) (*linodego.InstanceConfigInterface, error)
3737

38+
ListInterfaces(ctx context.Context, linodeID int, opts *linodego.ListOptions) ([]linodego.LinodeInterface, error)
39+
UpdateInterface(ctx context.Context, linodeID int, interfaceID int, opts linodego.LinodeInterfaceUpdateOptions) (*linodego.LinodeInterface, error)
40+
3841
GetVPC(context.Context, int) (*linodego.VPC, error)
3942
GetVPCSubnet(context.Context, int, int) (*linodego.VPCSubnet, error)
4043
ListVPCs(context.Context, *linodego.ListOptions) ([]linodego.VPC, error)

cloud/linode/client/client_with_metrics.go

Lines changed: 26 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

cloud/linode/client/mocks/mock_client.go

Lines changed: 30 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

cloud/linode/cloud.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,7 @@ var Options struct {
4949
LinodeExternalNetwork *net.IPNet
5050
NodeBalancerTags []string
5151
DefaultNBType string
52+
UseLinodeInterfaces bool
5253
NodeBalancerBackendIPv4Subnet string
5354
NodeBalancerBackendIPv4SubnetID int
5455
NodeBalancerBackendIPv4SubnetName string

cloud/linode/nodeipamcontroller.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -87,6 +87,7 @@ func startNodeIpamController(stopCh <-chan struct{}, cloud *linodeCloud, nodeInf
8787
nodeCIDRMaskSizes,
8888
ipam.CloudAllocatorType,
8989
Options.DisableIPv6NodeCIDRAllocation,
90+
Options.UseLinodeInterfaces,
9091
)
9192
if err != nil {
9293
return err

cloud/linode/route_controller.go

Lines changed: 58 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -59,9 +59,10 @@ func (rc *routeCache) refreshRoutes(ctx context.Context, client client.Client) {
5959
}
6060

6161
type routes struct {
62-
client client.Client
63-
instances *instances
64-
routeCache *routeCache
62+
client client.Client
63+
instances *instances
64+
routeCache *routeCache
65+
useLinodeInterfaces bool
6566
}
6667

6768
func newRoutes(client client.Client, instanceCache *instances) (cloudprovider.Routes, error) {
@@ -84,6 +85,7 @@ func newRoutes(client client.Client, instanceCache *instances) (cloudprovider.Ro
8485
routes: make(map[int][]linodego.VPCIP, 0),
8586
ttl: time.Duration(timeout) * time.Second,
8687
},
88+
useLinodeInterfaces: Options.UseLinodeInterfaces,
8789
}, nil
8890
}
8991

@@ -154,6 +156,7 @@ func (r *routes) CreateRoute(ctx context.Context, clusterName string, nameHint s
154156

155157
// check already configured routes
156158
intfRoutes := []string{}
159+
linodeInterfaceRoutes := []linodego.VPCInterfaceIPv4RangeCreateOptions{}
157160
intfVPCIP := linodego.VPCIP{}
158161

159162
for _, vpcid := range GetAllVPCIDs() {
@@ -173,6 +176,9 @@ func (r *routes) CreateRoute(ctx context.Context, clusterName string, nameHint s
173176
}
174177

175178
intfRoutes = append(intfRoutes, *ir.AddressRange)
179+
linodeInterfaceRoutes = append(linodeInterfaceRoutes, linodego.VPCInterfaceIPv4RangeCreateOptions{
180+
Range: *ir.AddressRange,
181+
})
176182
}
177183
}
178184

@@ -181,15 +187,32 @@ func (r *routes) CreateRoute(ctx context.Context, clusterName string, nameHint s
181187
}
182188

183189
intfRoutes = append(intfRoutes, route.DestinationCIDR)
184-
interfaceUpdateOptions := linodego.InstanceConfigInterfaceUpdateOptions{
185-
IPRanges: &intfRoutes,
190+
linodeInterfaceRoutes = append(linodeInterfaceRoutes, linodego.VPCInterfaceIPv4RangeCreateOptions{
191+
Range: route.DestinationCIDR,
192+
})
193+
194+
if Options.UseLinodeInterfaces {
195+
interfaceUpdateOptions := linodego.LinodeInterfaceUpdateOptions{
196+
VPC: &linodego.VPCInterfaceCreateOptions{
197+
IPv4: &linodego.VPCInterfaceIPv4CreateOptions{Ranges: linodeInterfaceRoutes},
198+
},
199+
}
200+
resp, err := r.client.UpdateInterface(ctx, instance.ID, intfVPCIP.InterfaceID, interfaceUpdateOptions)
201+
if err != nil {
202+
return err
203+
}
204+
klog.V(4).Infof("Added routes for node %s. Current routes: %v", route.TargetNode, resp.VPC.IPv4.Ranges)
205+
} else {
206+
interfaceUpdateOptions := linodego.InstanceConfigInterfaceUpdateOptions{
207+
IPRanges: &intfRoutes,
208+
}
209+
resp, err := r.client.UpdateInstanceConfigInterface(ctx, instance.ID, intfVPCIP.ConfigID, intfVPCIP.InterfaceID, interfaceUpdateOptions)
210+
if err != nil {
211+
return err
212+
}
213+
klog.V(4).Infof("Added routes for node %s. Current routes: %v", route.TargetNode, resp.IPRanges)
186214
}
187215

188-
resp, err := r.client.UpdateInstanceConfigInterface(ctx, instance.ID, intfVPCIP.ConfigID, intfVPCIP.InterfaceID, interfaceUpdateOptions)
189-
if err != nil {
190-
return err
191-
}
192-
klog.V(4).Infof("Added routes for node %s. Current routes: %v", route.TargetNode, resp.IPRanges)
193216
return nil
194217
}
195218

@@ -207,6 +230,7 @@ func (r *routes) DeleteRoute(ctx context.Context, clusterName string, route *clo
207230

208231
// check already configured routes
209232
intfRoutes := []string{}
233+
linodeInterfaceRoutes := []linodego.VPCInterfaceIPv4RangeCreateOptions{}
210234
intfVPCIP := linodego.VPCIP{}
211235

212236
for _, vpcid := range GetAllVPCIDs() {
@@ -225,21 +249,38 @@ func (r *routes) DeleteRoute(ctx context.Context, clusterName string, route *clo
225249
}
226250

227251
intfRoutes = append(intfRoutes, *ir.AddressRange)
252+
linodeInterfaceRoutes = append(linodeInterfaceRoutes, linodego.VPCInterfaceIPv4RangeCreateOptions{
253+
Range: *ir.AddressRange,
254+
})
228255
}
229256
}
230257

231258
if intfVPCIP.Address == nil {
232259
return fmt.Errorf("unable to remove route %s for node %s. no valid interface found", route.DestinationCIDR, route.TargetNode)
233260
}
234261

235-
interfaceUpdateOptions := linodego.InstanceConfigInterfaceUpdateOptions{
236-
IPRanges: &intfRoutes,
237-
}
238-
resp, err := r.client.UpdateInstanceConfigInterface(ctx, instance.ID, intfVPCIP.ConfigID, intfVPCIP.InterfaceID, interfaceUpdateOptions)
239-
if err != nil {
240-
return err
262+
if Options.UseLinodeInterfaces {
263+
interfaceUpdateOptions := linodego.LinodeInterfaceUpdateOptions{
264+
VPC: &linodego.VPCInterfaceCreateOptions{
265+
IPv4: &linodego.VPCInterfaceIPv4CreateOptions{Ranges: linodeInterfaceRoutes},
266+
},
267+
}
268+
resp, err := r.client.UpdateInterface(ctx, instance.ID, intfVPCIP.InterfaceID, interfaceUpdateOptions)
269+
if err != nil {
270+
return err
271+
}
272+
klog.V(4).Infof("Deleted route for node %s. Current routes: %v", route.TargetNode, resp.VPC.IPv4.Ranges)
273+
} else {
274+
interfaceUpdateOptions := linodego.InstanceConfigInterfaceUpdateOptions{
275+
IPRanges: &intfRoutes,
276+
}
277+
resp, err := r.client.UpdateInstanceConfigInterface(ctx, instance.ID, intfVPCIP.ConfigID, intfVPCIP.InterfaceID, interfaceUpdateOptions)
278+
if err != nil {
279+
return err
280+
}
281+
klog.V(4).Infof("Deleted route for node %s. Current routes: %v", route.TargetNode, resp.IPRanges)
241282
}
242-
klog.V(4).Infof("Deleted route for node %s. Current routes: %v", route.TargetNode, resp.IPRanges)
283+
243284
return nil
244285
}
245286

cloud/linode/route_controller_test.go

Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -323,6 +323,7 @@ func TestCreateRoute(t *testing.T) {
323323
Options.VPCNames = []string{"dummy"}
324324
vpcIDs["dummy"] = 1
325325
Options.EnableRouteController = true
326+
Options.UseLinodeInterfaces = false
326327

327328
nodeID := 123
328329
name := "mock-instance"
@@ -390,6 +391,38 @@ func TestCreateRoute(t *testing.T) {
390391
assert.NoError(t, err)
391392
})
392393

394+
interfaceWithVPCAndRoute := linodego.LinodeInterface{
395+
ID: vpcIDs["dummy"],
396+
VPC: &linodego.VPCInterface{
397+
IPv4: linodego.VPCInterfaceIPv4{
398+
Ranges: []linodego.VPCInterfaceIPv4Range{{Range: "10.10.10.0/24"}},
399+
},
400+
},
401+
}
402+
Options.UseLinodeInterfaces = true
403+
t.Run("should return no error if instance exists, connected to VPC we add a route with linode interfaces", func(t *testing.T) {
404+
ctrl := gomock.NewController(t)
405+
defer ctrl.Finish()
406+
client := mocks.NewMockClient(ctrl)
407+
instanceCache := newInstances(client)
408+
existingK8sCache := registeredK8sNodeCache
409+
defer func() {
410+
registeredK8sNodeCache = existingK8sCache
411+
}()
412+
registeredK8sNodeCache = newK8sNodeCache()
413+
registeredK8sNodeCache.addNodeToCache(node)
414+
routeController, err := newRoutes(client, instanceCache)
415+
require.NoError(t, err)
416+
417+
client.EXPECT().ListInstances(gomock.Any(), nil).Times(1).Return([]linodego.Instance{validInstance}, nil)
418+
client.EXPECT().ListVPCIPAddresses(gomock.Any(), gomock.Any(), gomock.Any()).Times(2).Return(noRoutesInVPC, nil)
419+
client.EXPECT().ListVPCIPv6Addresses(gomock.Any(), gomock.Any(), gomock.Any()).AnyTimes().Return([]linodego.VPCIP{}, nil)
420+
client.EXPECT().UpdateInterface(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Times(1).Return(&interfaceWithVPCAndRoute, nil)
421+
err = routeController.CreateRoute(ctx, "dummy", "dummy", route)
422+
assert.NoError(t, err)
423+
})
424+
Options.UseLinodeInterfaces = false
425+
393426
v6Route := &cloudprovider.Route{
394427
Name: "route2",
395428
TargetNode: types.NodeName(name),
@@ -477,6 +510,7 @@ func TestDeleteRoute(t *testing.T) {
477510
Options.VPCNames = []string{"dummy"}
478511
vpcIDs["dummy"] = 1
479512
Options.EnableRouteController = true
513+
Options.UseLinodeInterfaces = false
480514

481515
ctx := t.Context()
482516

@@ -549,6 +583,29 @@ func TestDeleteRoute(t *testing.T) {
549583
assert.NoError(t, err)
550584
})
551585

586+
interfaceWitVPCAndNoRoute := linodego.LinodeInterface{
587+
ID: vpcIDs["dummy"],
588+
VPC: &linodego.VPCInterface{IPv4: linodego.VPCInterfaceIPv4{Ranges: nil}},
589+
}
590+
591+
Options.UseLinodeInterfaces = true
592+
t.Run("should return no error if instance exists, connected to VPC, route doesn't exist and we try to delete route with linode interfaces", func(t *testing.T) {
593+
ctrl := gomock.NewController(t)
594+
defer ctrl.Finish()
595+
client := mocks.NewMockClient(ctrl)
596+
instanceCache := newInstances(client)
597+
routeController, err := newRoutes(client, instanceCache)
598+
require.NoError(t, err)
599+
600+
client.EXPECT().ListInstances(gomock.Any(), nil).Times(1).Return([]linodego.Instance{validInstance}, nil)
601+
client.EXPECT().ListVPCIPAddresses(gomock.Any(), gomock.Any(), gomock.Any()).Times(2).Return(noRoutesInVPC, nil)
602+
client.EXPECT().ListVPCIPv6Addresses(gomock.Any(), gomock.Any(), gomock.Any()).AnyTimes().Return([]linodego.VPCIP{}, nil)
603+
client.EXPECT().UpdateInterface(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Times(1).Return(&interfaceWitVPCAndNoRoute, nil)
604+
err = routeController.DeleteRoute(ctx, "dummy", route)
605+
assert.NoError(t, err)
606+
})
607+
Options.UseLinodeInterfaces = false
608+
552609
routesInVPC := []linodego.VPCIP{
553610
{
554611
Address: &vpcIP,
@@ -581,4 +638,21 @@ func TestDeleteRoute(t *testing.T) {
581638
err = routeController.DeleteRoute(ctx, "dummy", route)
582639
assert.NoError(t, err)
583640
})
641+
642+
Options.UseLinodeInterfaces = true
643+
t.Run("should return no error if instance exists, connected to VPC and route is deleted with linode interfaces", func(t *testing.T) {
644+
ctrl := gomock.NewController(t)
645+
defer ctrl.Finish()
646+
client := mocks.NewMockClient(ctrl)
647+
instanceCache := newInstances(client)
648+
routeController, err := newRoutes(client, instanceCache)
649+
require.NoError(t, err)
650+
651+
client.EXPECT().ListInstances(gomock.Any(), nil).Times(1).Return([]linodego.Instance{validInstance}, nil)
652+
client.EXPECT().ListVPCIPAddresses(gomock.Any(), gomock.Any(), gomock.Any()).Times(2).Return(routesInVPC, nil)
653+
client.EXPECT().ListVPCIPv6Addresses(gomock.Any(), gomock.Any(), gomock.Any()).AnyTimes().Return([]linodego.VPCIP{}, nil)
654+
client.EXPECT().UpdateInterface(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Times(1).Return(&interfaceWitVPCAndNoRoute, nil)
655+
err = routeController.DeleteRoute(ctx, "dummy", route)
656+
assert.NoError(t, err)
657+
})
584658
}

cloud/nodeipam/ipam/cidr_allocator.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -94,6 +94,8 @@ type CIDRAllocatorParams struct {
9494
// NodeCIDRMaskSizes is list of node cidr mask sizes.
9595
NodeCIDRMaskSizes []int
9696
DisableIPv6NodeCIDRAllocation bool
97+
// UseLinodeInterfaces indicates whether to use Linode interfaces instead of legacy configuration interfaces.
98+
UseLinodeInterfaces bool
9799
}
98100

99101
// New creates a new CIDR range allocator.

0 commit comments

Comments
 (0)