Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
added gateway routing manager implementation and test suite for the g…
…ateway operator.
- Loading branch information
Showing
3 changed files
with
319 additions
and
2 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,84 @@ | ||
package routing | ||
|
||
import ( | ||
"strconv" | ||
|
||
"github.com/vishvananda/netlink" | ||
"golang.org/x/sys/unix" | ||
|
||
netv1alpha1 "github.com/liqotech/liqo/apis/net/v1alpha1" | ||
"github.com/liqotech/liqo/pkg/liqonet" | ||
liqoerrors "github.com/liqotech/liqo/pkg/liqonet/errors" | ||
) | ||
|
||
// GatewayRoutingManager implements the routing manager interface. | ||
// Used by the gateway operator to configure the routes for remote clusters. | ||
type GatewayRoutingManager struct { | ||
routingTableID int | ||
tunnelDevice netlink.Link | ||
} | ||
|
||
// NewGatewayRoutingManager returns a GatewayRoutingManager ready to be used or an error. | ||
func NewGatewayRoutingManager(routingTableID int, tunnelDevice netlink.Link) (Routing, error) { | ||
// Check the validity of input parameters. | ||
if routingTableID > unix.RT_TABLE_MAX { | ||
return nil, &liqoerrors.WrongParameter{Parameter: "routingTableID", Reason: liqoerrors.MinorOrEqual + strconv.Itoa(unix.RT_TABLE_MAX)} | ||
} | ||
if routingTableID < 0 { | ||
return nil, &liqoerrors.WrongParameter{Parameter: "routingTableID", Reason: liqoerrors.GreaterOrEqual + strconv.Itoa(0)} | ||
} | ||
if tunnelDevice == nil { | ||
return nil, &liqoerrors.WrongParameter{ | ||
Reason: liqoerrors.NotNil, | ||
Parameter: "tunnelDevice", | ||
} | ||
} | ||
return &GatewayRoutingManager{ | ||
routingTableID: routingTableID, | ||
tunnelDevice: tunnelDevice, | ||
}, nil | ||
} | ||
|
||
// EnsureRoutesPerCluster accepts as input a netv1alpha.tunnelendpoint. | ||
// It inserts the routes if they do not exist or updates them if they are outdated. | ||
// Returns true if the routes have been configured, false if the routes are already configured. | ||
// An error if something goes wrong and the routes can not be configured. | ||
func (grm *GatewayRoutingManager) EnsureRoutesPerCluster(tep *netv1alpha1.TunnelEndpoint) (bool, error) { | ||
var routeAdd bool | ||
var err error | ||
// Extract and save route information from the given tep. | ||
_, dstNet := liqonet.GetPodCIDRS(tep) | ||
// Add route for the given cluster. | ||
routeAdd, err = AddRoute(dstNet, "", grm.tunnelDevice.Attrs().Index, grm.routingTableID) | ||
if err != nil { | ||
return routeAdd, err | ||
} | ||
return routeAdd, nil | ||
} | ||
|
||
// RemoveRoutesPerCluster accepts as input a netv1alpha.tunnelendpoint. | ||
// It deletes the routes if they do exist. | ||
// Returns true if the routes exist and have been deleted, false if nothing is removed. | ||
// An error if something goes wrong and the routes can not be removed. | ||
func (grm *GatewayRoutingManager) RemoveRoutesPerCluster(tep *netv1alpha1.TunnelEndpoint) (bool, error) { | ||
var routeDel bool | ||
var err error | ||
// Extract and save route information from the given tep. | ||
_, dstNet := liqonet.GetPodCIDRS(tep) | ||
// Delete route for the given cluster. | ||
routeDel, err = delRoute(dstNet, "", grm.tunnelDevice.Attrs().Index, grm.routingTableID) | ||
if err != nil { | ||
return routeDel, err | ||
} | ||
return routeDel, nil | ||
} | ||
|
||
// CleanRoutingTable stub function, as the gateway only operates in custom network namespace. | ||
func (grm *GatewayRoutingManager) CleanRoutingTable() error { | ||
return flushRoutesForRoutingTable(grm.routingTableID) | ||
} | ||
|
||
// CleanPolicyRules stub function, as the gateway only operates in custom network namespace. | ||
func (grm *GatewayRoutingManager) CleanPolicyRules() error { | ||
return flushRulesForRoutingTable(grm.routingTableID) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,219 @@ | ||
package routing | ||
|
||
import ( | ||
"net" | ||
"strconv" | ||
|
||
. "github.com/onsi/ginkgo" | ||
. "github.com/onsi/gomega" | ||
"github.com/vishvananda/netlink" | ||
"golang.org/x/sys/unix" | ||
|
||
netv1alpha1 "github.com/liqotech/liqo/apis/net/v1alpha1" | ||
liqoerrors "github.com/liqotech/liqo/pkg/liqonet/errors" | ||
) | ||
|
||
var ( | ||
routingTableIDGRM = 19773 | ||
routesGRM = []routingInfo{ | ||
{ | ||
destinationNet: "10.120.0.0/16", | ||
routingTableID: routingTableIDGRM, | ||
}, | ||
{ | ||
destinationNet: "10.121.0.0/16", | ||
routingTableID: routingTableIDGRM, | ||
}} | ||
existingRoutesGRM []*netlink.Route | ||
tepGRM netv1alpha1.TunnelEndpoint | ||
tunnelDevice netlink.Link | ||
) | ||
|
||
var _ = Describe("GatewayRouting", func() { | ||
BeforeEach(func() { | ||
// Populate the index of the routes with the correct one. | ||
for i := range routesGRM { | ||
routesGRM[i].iFaceIndex = tunnelDevice.Attrs().Index | ||
} | ||
}) | ||
|
||
Describe("creating new Gateway Route Manager", func() { | ||
|
||
Context("when parameters are not valid", func() { | ||
It("routingTableID parameter out of range: a negative number", func() { | ||
grm, err := NewGatewayRoutingManager(-244, tunnelDevice) | ||
Expect(grm).Should(BeNil()) | ||
Expect(err).Should(Equal(&liqoerrors.WrongParameter{Parameter: "routingTableID", Reason: liqoerrors.GreaterOrEqual + strconv.Itoa(0)})) | ||
}) | ||
|
||
It("routingTableID parameter out of range: superior to max value ", func() { | ||
grm, err := NewGatewayRoutingManager(unix.RT_TABLE_MAX+1, tunnelDevice) | ||
Expect(grm).Should(BeNil()) | ||
Expect(err).Should(Equal(&liqoerrors.WrongParameter{Parameter: "routingTableID", Reason: liqoerrors.MinorOrEqual + strconv.Itoa(unix.RT_TABLE_MAX)})) | ||
}) | ||
It("tunnelDevice is nil", func() { | ||
grm, err := NewGatewayRoutingManager(routingTableIDGRM, nil) | ||
Expect(grm).Should(BeNil()) | ||
Expect(err).Should(Equal(&liqoerrors.WrongParameter{Parameter: "tunnelDevice", Reason: liqoerrors.NotNil})) | ||
}) | ||
}) | ||
|
||
Context("when parameters are correct", func() { | ||
It("right parameters", func() { | ||
grm, err := NewGatewayRoutingManager(routingTableIDGRM, tunnelDevice) | ||
Expect(grm).ShouldNot(BeNil()) | ||
Expect(err).ShouldNot(HaveOccurred()) | ||
}) | ||
}) | ||
}) | ||
|
||
Describe("configuring routes for a remote peering cluster", func() { | ||
JustBeforeEach(func() { | ||
tepGRM = netv1alpha1.TunnelEndpoint{ | ||
Status: netv1alpha1.TunnelEndpointStatus{ | ||
LocalNATPodCIDR: "10.150.0.0/16", | ||
RemoteNATPodCIDR: "10.250.0.0/16", | ||
VethIFaceIndex: 12345, | ||
GatewayIP: ipAddress2NoSubnet, | ||
}} | ||
}) | ||
Context("when tep holds malformed parameters", func() { | ||
It("route configuration fails while adding route", func() { | ||
tepGRM.Status.RemoteNATPodCIDR = "10.150.000/16" | ||
added, err := grm.EnsureRoutesPerCluster(&tepGRM) | ||
Expect(err).Should(Equal(&net.ParseError{ | ||
Type: "CIDR address", | ||
Text: "10.150.000/16", | ||
})) | ||
Expect(added).Should(BeFalse()) | ||
Expect(err).NotTo(BeNil()) | ||
}) | ||
}) | ||
|
||
Context("when tep holds correct parameters", func() { | ||
JustBeforeEach(func() { | ||
existingRoutesGRM = setUpRoutes(routesGRM) | ||
}) | ||
|
||
JustAfterEach(func() { | ||
tearDownRoutes(routingTableIDGRM) | ||
}) | ||
|
||
It("route configuration should be correctly inserted", func() { | ||
added, err := grm.EnsureRoutesPerCluster(&tep) | ||
Expect(err).ShouldNot(HaveOccurred()) | ||
Expect(added).Should(BeTrue()) | ||
// Get the inserted route | ||
_, dstNet, err := net.ParseCIDR(tep.Status.RemoteNATPodCIDR) | ||
Expect(err).ShouldNot(HaveOccurred()) | ||
routes, err := netlink.RouteListFiltered(netlink.FAMILY_V4, &netlink.Route{Dst: dstNet, Table: routingTableIDGRM}, netlink.RT_FILTER_DST|netlink.RT_FILTER_TABLE) | ||
Expect(err).ShouldNot(HaveOccurred()) | ||
Expect(routes[0].Dst.String()).Should(Equal(tep.Status.RemoteNATPodCIDR)) | ||
Expect(routes[0].Gw).Should(BeNil()) | ||
}) | ||
|
||
It("route already exists, should return false and nil", func() { | ||
tepGRM.Status.RemoteNATPodCIDR = existingRoutesGRM[0].Dst.String() | ||
tepGRM.Status.GatewayIP = existingRoutesGRM[0].Gw.String() | ||
added, err := grm.EnsureRoutesPerCluster(&tepGRM) | ||
Expect(err).ShouldNot(HaveOccurred()) | ||
Expect(added).Should(BeFalse()) | ||
}) | ||
}) | ||
}) | ||
|
||
Describe("removing route configuration for a remote peering cluster", func() { | ||
Context("when tep holds malformed parameters", func() { | ||
It("fails to remove route configuration while removing the route", func() { | ||
tepGRM.Status.RemoteNATPodCIDR = "" | ||
added, err := grm.RemoveRoutesPerCluster(&tepGRM) | ||
Expect(err).Should(Equal(&net.ParseError{ | ||
Type: "CIDR address", | ||
Text: "", | ||
})) | ||
Expect(added).Should(BeFalse()) | ||
Expect(err).NotTo(BeNil()) | ||
}) | ||
}) | ||
|
||
Context("when tep holds correct parameters", func() { | ||
JustBeforeEach(func() { | ||
existingRoutesGRM = setUpRoutes(routesGRM) | ||
}) | ||
|
||
JustAfterEach(func() { | ||
tearDownRoutes(routingTableIDGRM) | ||
}) | ||
|
||
It("route configuration should be correctly removed", func() { | ||
tepGRM.Status.RemoteNATPodCIDR = existingRoutesGRM[0].Dst.String() | ||
deleted, err := grm.RemoveRoutesPerCluster(&tepGRM) | ||
Expect(err).ShouldNot(HaveOccurred()) | ||
Expect(deleted).Should(BeTrue()) | ||
// Try to get the remove route. | ||
routes, err := netlink.RouteListFiltered(netlink.FAMILY_V4, existingRoutesGRM[0], netlink.RT_FILTER_DST|netlink.RT_FILTER_TABLE) | ||
Expect(err).ShouldNot(HaveOccurred()) | ||
Expect(len(routes)).Should(BeZero()) | ||
}) | ||
|
||
It("route configuration should be correctly removed from veth pair", func() { | ||
tepGRM.Status.RemoteNATPodCIDR = existingRoutesGRM[1].Dst.String() | ||
tepGRM.Status.GatewayIP = ipAddress1NoSubnet | ||
tepGRM.Status.VethIFaceIndex = overlayDevice.Link.Index | ||
added, err := grm.RemoveRoutesPerCluster(&tepGRM) | ||
Expect(err).ShouldNot(HaveOccurred()) | ||
Expect(added).Should(BeTrue()) | ||
// Try to get the remove route. | ||
routes, err := netlink.RouteListFiltered(netlink.FAMILY_V4, existingRoutesGRM[1], netlink.RT_FILTER_DST|netlink.RT_FILTER_TABLE) | ||
Expect(err).ShouldNot(HaveOccurred()) | ||
Expect(len(routes)).Should(BeZero()) | ||
}) | ||
|
||
It("route does not exist, should return false and nil", func() { | ||
added, err := grm.RemoveRoutesPerCluster(&tep) | ||
Expect(err).ShouldNot(HaveOccurred()) | ||
Expect(added).Should(BeFalse()) | ||
}) | ||
}) | ||
}) | ||
|
||
Describe("removing all routes configurations managed by the gateway route manager", func() { | ||
Context("removing routes, should return nil", func() { | ||
JustBeforeEach(func() { | ||
existingRoutesGRM = setUpRoutes(routesGRM) | ||
}) | ||
|
||
JustAfterEach(func() { | ||
tearDownRoutes(routingTableIDGRM) | ||
}) | ||
|
||
It("routes should be correctly removed", func() { | ||
err := grm.CleanRoutingTable() | ||
Expect(err).ShouldNot(HaveOccurred()) | ||
// Try to list rules | ||
routes, err := netlink.RouteListFiltered(netlink.FAMILY_V4, existingRoutesGRM[0], netlink.RT_FILTER_TABLE) | ||
Expect(err).ShouldNot(HaveOccurred()) | ||
Expect(len(routes)).Should(BeZero()) | ||
}) | ||
}) | ||
|
||
Context("removing policy routing rules, should return nil", func() { | ||
JustBeforeEach(func() { | ||
existingRoutesGRM = setUpRoutes(routesGRM) | ||
}) | ||
|
||
JustAfterEach(func() { | ||
tearDownRoutes(routingTableIDGRM) | ||
}) | ||
|
||
It("policy routing rules should be correctly removed", func() { | ||
err := grm.CleanPolicyRules() | ||
Expect(err).ShouldNot(HaveOccurred()) | ||
// Try to list rules | ||
exists, err := existsRuleForRoutingTable(routingTableIDGRM) | ||
Expect(err).ShouldNot(HaveOccurred()) | ||
Expect(exists).Should(BeFalse()) | ||
}) | ||
}) | ||
}) | ||
}) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters